Projet

Général

Profil

SpeADL Dynamic Tutorial » Historique » Version 10

Anonyme, 14/10/2014 16:56

1 3 Anonyme
h1. SpeADL for Dynamic Architectures Tutorial
2 2 Anonyme
3 7 Anonyme
{{>toc}}
4
5 1 Anonyme
This is a tutorial for SpeADL: understanding ecosystems and species, defining a simple ecosystem, composing species together with uses.
6 3 Anonyme
7
h2. Objectives
8
9
The objective of this tutorial to understand the abstractions of species and uses.
10
11
We will create a Logging ecosystem that contains Logger species.
12
Then we will create a Banking ecosystem that contains Account species that need to log what happens.
13
Finally we will create a Bank ecosystem that compose Account species with Logger species thanks to the use abstraction.
14
15
h2. Prerequisites
16
17
It is needed to understand the content of the [[SpeADL Minus Tutorial|SpeADL⁻ tutorial]] before doing this one.
18
19
h2. Creating a New Project and Organisation
20
21
Create a Java project.
22
We will use again an organisation of the package and namespaces as explained in [[MAY Best Practices#Project Organisation|this best practice]].
23
24
h2. The Logging Ecosystem
25
26
Logging will be an ecosystem, and its particularity is that it will be able to create Loggers: while logging is the subsystem responsible of all logging, each logger will be responsible of logging one particular aspect of the system identified by a _name_.
27
We will have multiple implementation for the Logging ecosystem:
28
* One with only one file where to log with each line prepended with the name.
29
* One with one file per Logger all in the same directory.
30
31 9 Anonyme
In any case, a port will be provided by Logging to create standalone instances of Logger.
32
33 3 Anonyme
h3. Defining the Ecosystem and the Species
34 1 Anonyme
35 8 Anonyme
Create a SpeADL file named _logging.speadl_ in the package _tutorial2.logging_.
36 3 Anonyme
37
Define in it the ecosystem and the species in the right namespace as well as the needed interface:
38
<pre>
39 8 Anonyme
import tutorial2.logging.interfaces.ILog
40 3 Anonyme
41
namespace tutorial2.logging {
42 1 Anonyme
	
43 9 Anonyme
	ecosystem Logging {		
44
		provides create: ICreateLogger
45
46 3 Anonyme
		species Logger(name: String) {
47
			provides log: ILog
48
		}
49
	}
50
	
51
}
52
</pre>
53
54
<pre>
55 8 Anonyme
package tutorial2.logging.interfaces;
56 3 Anonyme
57
public interface ILog {
58
59
	public void addLine(String line);
60 1 Anonyme
}
61 9 Anonyme
62
public interface ICreateLogger {
63
64
	public Logging.Logger.Component createStandaloneLogger(String name);
65
}
66 3 Anonyme
</pre>
67
68
h3. Implementing the Ecosystem and the Species, First One
69
70 4 Anonyme
Create a new Java class in _tutorial2.logging.impl_ named _LoggingImplOne_ that extends _Logging_, that takes the a _File_ as a parameter to the constructor to store the logs, and resolve the error with the Quick Fixes of Eclipse:
71 3 Anonyme
<pre>
72
package tutorial2.logging.impl;
73
74
import java.io.File;
75
import java.io.FileNotFoundException;
76 5 Anonyme
import java.io.FileWriter;
77 3 Anonyme
import java.io.IOException;
78
import java.io.PrintWriter;
79
80 1 Anonyme
import tutorial2.logging.Logging;
81 9 Anonyme
import tutorial2.logging.interfaces.ICreateLogger;
82 3 Anonyme
83
public class LoggingImplOne extends Logging {
84
85
	private PrintWriter logWriter;
86
	
87
	public LoggingImplOne(File logFile) {
88
		// if an exception happens, nothing can be done about it
89
		// we just let logStream be null and
90
		// the operations of logging won't be done
91
		try {
92 5 Anonyme
			this.logWriter = new PrintWriter(new FileWriter(logFile), true);
93 3 Anonyme
		} catch (FileNotFoundException e) {
94 5 Anonyme
			System.err.println("An error happened with the file, nothing will be logged.");
95 1 Anonyme
			this.logWriter = null;
96 3 Anonyme
		} catch (IOException e) {
97 5 Anonyme
			System.err.println("An error happened with the file, nothing will be logged.");
98 3 Anonyme
			this.logWriter = null;
99
		}
100 1 Anonyme
	}
101 9 Anonyme
102
	@Override
103
	protected ICreateLogger make_create() {
104
		// TODO Auto-generated method stub
105
		return null;
106
	}
107 3 Anonyme
	
108
	@Override
109
	protected Logger make_Logger(String name) {
110
		// TODO Auto-generated method stub
111
		return null;
112
	}
113
}
114
</pre>
115
116
As we can see, the species we defined needs to be implemented and this implementation needs to be returned by the method _Logger make_Logger(String name)_.
117
118 4 Anonyme
Let's define an inner class inside _LoggingImplOne_ to do that, it will take as a parameter to the constructor the _name_ of the Logger and that will exploit the _logWriter_ to do the actual logging.
119 3 Anonyme
<pre>
120
public class LoggingImplOne extends Logging {
121
122
	private PrintWriter logWriter;
123
	
124
	// ...
125
	
126
	@Override
127
	protected Logger make_Logger(String name) {
128
		return new LoggerImpl(name);
129
	}
130
	
131
	private class LoggerImpl extends Logger implements ILog {
132
133
		private final String name;
134
		
135
		public LoggerImpl(String name) {
136
			this.name = name;
137
		}
138
		
139
		@Override
140
		protected ILog make_log() {
141
			return this;
142 1 Anonyme
		}
143
144
		@Override
145 5 Anonyme
		public void addLine(String line) {
146 1 Anonyme
			if (logWriter != null) {
147 5 Anonyme
				logWriter.println("["+name+"] "+ line);
148
			}
149
		}
150
	}
151
}
152
</pre>
153 1 Anonyme
154 9 Anonyme
Then let's implement the create port by exploiting the method *newLogger(String name)* present in the extended class that enables to instantiate a species:
155
<pre>
156
public class LoggingImplOne extends Logging {
157
158
	// ...
159
	
160
	@Override
161
	protected ICreateLogger make_create() {
162
		return new ICreateLogger() {
163
			@Override
164
			public Logger.Component createStandaloneLogger(String name) {
165
				return newLogger(name);
166
			}
167
		};
168
	}
169
170
	// ...
171
}
172 10 Anonyme
</pre>
173 9 Anonyme
174 5 Anonyme
We now have a complete implementation for the Logging ecosystem.
175 9 Anonyme
Let's test it with a very simple program that we put in the package _tutorial2.logging.tests_*:
176 5 Anonyme
<pre>
177
package tutorial2.logging.tests;
178
179 1 Anonyme
import java.io.File;
180
181 9 Anonyme
import tutorial2.logging.Logging;
182
import tutorial2.logging.Logging.Logger;
183 1 Anonyme
import tutorial2.logging.impl.LoggingImplOne;
184 5 Anonyme
185 9 Anonyme
public class LoggingTest {
186
	public static void main(String[] args) {
187 5 Anonyme
188 9 Anonyme
		Logging.Component logging = new LoggingImplOne(new File("/tmp/tutorial2-logging-test.txt")).newComponent();
189 5 Anonyme
190
		// create new Loggers
191 9 Anonyme
		Logger.Component l1 = logging.create().createStandaloneLogger("a");
192
		Logger.Component l2 = logging.create().createStandaloneLogger("b");
193
		Logger.Component l3 = logging.create().createStandaloneLogger("c");
194
195 5 Anonyme
		// log things to them
196
		l1.log().addLine("1 a says hi");
197
		l1.log().addLine("2 test test");
198
		l2.log().addLine("3 b says hoy");
199
		l1.log().addLine("4 blabla");
200
		l3.log().addLine("5 hop");
201 6 Anonyme
	}
202 5 Anonyme
}
203
</pre>
204
205
Execute it and confirm that the file _/tmp/tutorial2-logging-test.txt_ contains the following:
206
<pre>
207
[a] 1 a says hi
208 3 Anonyme
[a] 2 test test
209
[b] 3 b says hoy
210 1 Anonyme
[a] 4 blabla
211
[c] 5 hop
212 6 Anonyme
</pre>
213
214
h3. Implementing the Ecosystem and the Species, Second One
215
216
We can now define a second implementation that stores the logs of each Logger in its own file:
217
<pre>
218
package tutorial2.logging.impl;
219
220
import java.io.File;
221
import java.io.FileNotFoundException;
222
import java.io.FileWriter;
223
import java.io.IOException;
224
import java.io.PrintWriter;
225
226
import tutorial2.logging.Logging;
227 9 Anonyme
import tutorial2.logging.interfaces.ICreateLogger;
228 1 Anonyme
import tutorial2.logging.interfaces.ILog;
229
230
public class LoggingImplTwo extends Logging {
231
232
	private final File logDir;
233
234
	public LoggingImplTwo(File logDir) {
235
		this.logDir = logDir;
236
		this.logDir.mkdirs();
237 6 Anonyme
	}
238
	
239
	@Override
240
	protected Logger make_Logger(String name) {
241
		return new LoggerImpl(new File(logDir, name+".txt"));
242
	}
243 9 Anonyme
244
	@Override
245
	protected ICreateLogger make_create() {
246
		return new ICreateLogger() {
247
			@Override
248
			public Logger.Component createStandaloneLogger(String name) {
249
				return newLogger(name);
250
			}
251
		};
252
	}
253 6 Anonyme
	
254
	private class LoggerImpl extends Logger implements ILog {
255
256
		private PrintWriter logWriter;
257
		
258
		public LoggerImpl(File logFile) {
259
			// if an exception happens, nothing can be done about it
260
			// we just let logStream be null and
261
			// the operations of logging won't be done
262
			try {
263
				this.logWriter = new PrintWriter(new FileWriter(logFile), true);
264
			} catch (FileNotFoundException e) {
265
				System.err.println("An error happened with the file, nothing will be logged.");
266
				this.logWriter = null;
267
			} catch (IOException e) {
268
				System.err.println("An error happened with the file, nothing will be logged.");
269
				this.logWriter = null;
270
			}
271 1 Anonyme
		}
272
		
273
		@Override
274
		protected ILog make_log() {
275
			return this;
276
		}
277
278
		@Override
279
		public void addLine(String line) {
280
			if (logWriter != null) {
281
				logWriter.println(line);
282
			}
283
		}
284
	}
285
}
286
</pre>
287
288 9 Anonyme
It can be tested with the previous class by changing the first line instantiating the component:
289 1 Anonyme
<pre>
290 9 Anonyme
Logging.Component logging = new LoggingImplTwo(new File("/tmp/tutorial2-logging-test/")).newComponent();
291
</pre>
292 1 Anonyme
293 9 Anonyme
Execute it and confirm that the _/tmp/tutorial2-logging-test/_ directory contains one file per Logger with the correct content.
294 1 Anonyme
295 9 Anonyme
h2. The Banking Ecosystem 
296 1 Anonyme
297 9 Anonyme
Banking will also be an ecosystem, it will contain Account that are species with a particularity: they have required ports!
298
Because of that, they can't simply be created but must be composed with other components or species to be usable.
299 1 Anonyme
300 9 Anonyme
h3. Defining the Ecosystem and the Species
301
302
Create a SpeADL file named _banking.speadl_ in the package _tutorial2.banking_ and define a _Banking_ ecosystem in _tutorial2.banking_ namespace:
303
<pre>
304
import tutorial2.banking.interfaces.IAccountOperations
305
import tutorial2.banking.interfaces.IAccountStatus
306
import tutorial2.logging.interfaces.ILog
307
308
namespace tutorial2.banking {
309 1 Anonyme
	
310 9 Anonyme
	ecosystem Banking {
311
		
312
		requires elog: ILog
313
		
314
		species Account(owner: String) {
315
			
316
			provides operations: IAccountOperations
317
			provides status: IAccountStatus
318
			
319
			requires log: ILog
320
		}
321 1 Anonyme
	}
322
}
323
</pre>
324
325 9 Anonyme
Then the interfaces in _tutorial2.banking.interfaces_:
326
<pre>
327
package tutorial2.banking.interfaces;
328 1 Anonyme
329 9 Anonyme
public interface IAccountOperations {
330 1 Anonyme
331 9 Anonyme
	public void deposit(int value);
332
	public void withdraw(int value);
333
}
334 1 Anonyme
335 9 Anonyme
public interface IAccountStatus {
336
337
	public int getBalance();
338
}
339
</pre>
340
341
h3. Implementing the Ecosystem and the Species
342
343
We now  can define an implementation named _BankingImpl_ in the package _tutorial2.banking.impl_.
344
It exploits the provided ports and the required ports of the species, but also the required port of the ecosystem:
345
<pre>
346
package tutorial2.banking.impl;
347
348
import java.util.concurrent.atomic.AtomicInteger;
349
350
import tutorial2.banking.Banking;
351
import tutorial2.banking.interfaces.IAccountOperations;
352
import tutorial2.banking.interfaces.IAccountStatus;
353
354
public class BankingImpl extends Banking {
355
	
356
	@Override
357
	protected Account make_Account(final String owner) {
358
		return new Account() {
359
			
360
			private final AtomicInteger balance = new AtomicInteger();
361
			
362
			@Override
363
			protected void start() {
364
				eco_requires().elog().addLine("Added a new account for "+owner);
365
			}
366
			
367
			@Override
368
			protected IAccountOperations make_operations() {
369
				return new IAccountOperations() {
370
					
371
					@Override
372
					public void withdraw(int value) {
373
						provides().operations().deposit(-value);
374
						requires().log().addLine(value+" were withdrawn, current balance: "+provides().status().getBalance());
375
					}
376
					
377
					@Override
378
					public void deposit(int value) {
379
						balance.addAndGet(value);
380
						requires().log().addLine(value+" were deposited, current balance: "+provides().status().getBalance());
381
					}
382
				};
383
			}
384
385
			@Override
386
			protected IAccountStatus make_status() {
387
				return new IAccountStatus() {
388
					@Override
389
					public int getBalance() {
390
						return balance.get();
391
					}
392
				};
393
			}
394
		};
395
	}
396
}
397
</pre>
398
399
Notice the calls to *provides()*, *requires()* and *eco_requires()* from within the species.