Projet

Général

Profil

SpeADL Dynamic Tutorial » Historique » Révision 10

Révision 9 (Anonyme, 14/10/2014 16:55) → Révision 10/37 (Anonyme, 14/10/2014 16:56)

h1. SpeADL for Dynamic Architectures Tutorial 

 {{>toc}} 

 This is a tutorial for SpeADL: understanding ecosystems and species, defining a simple ecosystem, composing species together with uses. 

 h2. Objectives 

 The objective of this tutorial to understand the abstractions of species and uses. 

 We will create a Logging ecosystem that contains Logger species. 
 Then we will create a Banking ecosystem that contains Account species that need to log what happens. 
 Finally we will create a Bank ecosystem that compose Account species with Logger species thanks to the use abstraction. 

 h2. Prerequisites 

 It is needed to understand the content of the [[SpeADL Minus Tutorial|SpeADL⁻ tutorial]] before doing this one. 

 h2. Creating a New Project and Organisation 

 Create a Java project. 
 We will use again an organisation of the package and namespaces as explained in [[MAY Best Practices#Project Organisation|this best practice]]. 

 h2. The Logging Ecosystem 

 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_. 
 We will have multiple implementation for the Logging ecosystem: 
 * One with only one file where to log with each line prepended with the name. 
 * One with one file per Logger all in the same directory. 

 In any case, a port will be provided by Logging to create standalone instances of Logger. 

 h3. Defining the Ecosystem and the Species 

 Create a SpeADL file named _logging.speadl_ in the package _tutorial2.logging_. 

 Define in it the ecosystem and the species in the right namespace as well as the needed interface: 
 <pre> 
 import tutorial2.logging.interfaces.ILog 

 namespace tutorial2.logging { 
	
	 ecosystem Logging { 		
		 provides create: ICreateLogger 

		 species Logger(name: String) { 
			 provides log: ILog 
		 } 
	 } 
	
 } 
 </pre> 

 <pre> 
 package tutorial2.logging.interfaces; 

 public interface ILog { 

	 public void addLine(String line); 
 } 

 public interface ICreateLogger { 

	 public Logging.Logger.Component createStandaloneLogger(String name); 
 } 
 </pre> 

 h3. Implementing the Ecosystem and the Species, First One 

 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: 
 <pre> 
 package tutorial2.logging.impl; 

 import java.io.File; 
 import java.io.FileNotFoundException; 
 import java.io.FileWriter; 
 import java.io.IOException; 
 import java.io.PrintWriter; 

 import tutorial2.logging.Logging; 
 import tutorial2.logging.interfaces.ICreateLogger; 

 public class LoggingImplOne extends Logging { 

	 private PrintWriter logWriter; 
	
	 public LoggingImplOne(File logFile) { 
		 // if an exception happens, nothing can be done about it 
		 // we just let logStream be null and 
		 // the operations of logging won't be done 
		 try { 
			 this.logWriter = new PrintWriter(new FileWriter(logFile), true); 
		 } catch (FileNotFoundException e) { 
			 System.err.println("An error happened with the file, nothing will be logged."); 
			 this.logWriter = null; 
		 } catch (IOException e) { 
			 System.err.println("An error happened with the file, nothing will be logged."); 
			 this.logWriter = null; 
		 } 
	 } 

	 @Override 
	 protected ICreateLogger make_create() { 
		 // TODO Auto-generated method stub 
		 return null; 
	 } 
	
	 @Override 
	 protected Logger make_Logger(String name) { 
		 // TODO Auto-generated method stub 
		 return null; 
	 } 
 } 
 </pre> 

 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)_. 

 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. 
 <pre> 
 public class LoggingImplOne extends Logging { 

	 private PrintWriter logWriter; 
	
	 // ... 
	
	 @Override 
	 protected Logger make_Logger(String name) { 
		 return new LoggerImpl(name); 
	 } 
	
	 private class LoggerImpl extends Logger implements ILog { 

		 private final String name; 
		
		 public LoggerImpl(String name) { 
			 this.name = name; 
		 } 
		
		 @Override 
		 protected ILog make_log() { 
			 return this; 
		 } 

		 @Override 
		 public void addLine(String line) { 
			 if (logWriter != null) { 
				 logWriter.println("["+name+"] "+ line); 
			 } 
		 } 
	 } 
 } 
 </pre> 

 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: 
 <pre> 
 public class LoggingImplOne extends Logging { 

	 // ... 
	
	 @Override 
	 protected ICreateLogger make_create() { 
		 return new ICreateLogger() { 
			 @Override 
			 public Logger.Component createStandaloneLogger(String name) { 
				 return newLogger(name); 
			 } 
		 }; 
	 } 

	 // ... 
 } 
 </pre> 

 


 We now have a complete implementation for the Logging ecosystem. 
 Let's test it with a very simple program that we put in the package _tutorial2.logging.tests_*: 
 <pre> 
 package tutorial2.logging.tests; 

 import java.io.File; 

 import tutorial2.logging.Logging; 
 import tutorial2.logging.Logging.Logger; 
 import tutorial2.logging.impl.LoggingImplOne; 

 public class LoggingTest { 
	 public static void main(String[] args) { 

		 Logging.Component logging = new LoggingImplOne(new File("/tmp/tutorial2-logging-test.txt")).newComponent(); 

		 // create new Loggers 
		 Logger.Component l1 = logging.create().createStandaloneLogger("a"); 
		 Logger.Component l2 = logging.create().createStandaloneLogger("b"); 
		 Logger.Component l3 = logging.create().createStandaloneLogger("c"); 

		 // log things to them 
		 l1.log().addLine("1 a says hi"); 
		 l1.log().addLine("2 test test"); 
		 l2.log().addLine("3 b says hoy"); 
		 l1.log().addLine("4 blabla"); 
		 l3.log().addLine("5 hop"); 
	 } 
 } 
 </pre> 

 Execute it and confirm that the file _/tmp/tutorial2-logging-test.txt_ contains the following: 
 <pre> 
 [a] 1 a says hi 
 [a] 2 test test 
 [b] 3 b says hoy 
 [a] 4 blabla 
 [c] 5 hop 
 </pre> 

 h3. Implementing the Ecosystem and the Species, Second One 

 We can now define a second implementation that stores the logs of each Logger in its own file: 
 <pre> 
 package tutorial2.logging.impl; 

 import java.io.File; 
 import java.io.FileNotFoundException; 
 import java.io.FileWriter; 
 import java.io.IOException; 
 import java.io.PrintWriter; 

 import tutorial2.logging.Logging; 
 import tutorial2.logging.interfaces.ICreateLogger; 
 import tutorial2.logging.interfaces.ILog; 

 public class LoggingImplTwo extends Logging { 

	 private final File logDir; 

	 public LoggingImplTwo(File logDir) { 
		 this.logDir = logDir; 
		 this.logDir.mkdirs(); 
	 } 
	
	 @Override 
	 protected Logger make_Logger(String name) { 
		 return new LoggerImpl(new File(logDir, name+".txt")); 
	 } 

	 @Override 
	 protected ICreateLogger make_create() { 
		 return new ICreateLogger() { 
			 @Override 
			 public Logger.Component createStandaloneLogger(String name) { 
				 return newLogger(name); 
			 } 
		 }; 
	 } 
	
	 private class LoggerImpl extends Logger implements ILog { 

		 private PrintWriter logWriter; 
		
		 public LoggerImpl(File logFile) { 
			 // if an exception happens, nothing can be done about it 
			 // we just let logStream be null and 
			 // the operations of logging won't be done 
			 try { 
				 this.logWriter = new PrintWriter(new FileWriter(logFile), true); 
			 } catch (FileNotFoundException e) { 
				 System.err.println("An error happened with the file, nothing will be logged."); 
				 this.logWriter = null; 
			 } catch (IOException e) { 
				 System.err.println("An error happened with the file, nothing will be logged."); 
				 this.logWriter = null; 
			 } 
		 } 
		
		 @Override 
		 protected ILog make_log() { 
			 return this; 
		 } 

		 @Override 
		 public void addLine(String line) { 
			 if (logWriter != null) { 
				 logWriter.println(line); 
			 } 
		 } 
	 } 
 } 
 </pre> 

 It can be tested with the previous class by changing the first line instantiating the component: 
 <pre> 
 Logging.Component logging = new LoggingImplTwo(new File("/tmp/tutorial2-logging-test/")).newComponent(); 
 </pre> 

 Execute it and confirm that the _/tmp/tutorial2-logging-test/_ directory contains one file per Logger with the correct content. 

 h2. The Banking Ecosystem  

 Banking will also be an ecosystem, it will contain Account that are species with a particularity: they have required ports! 
 Because of that, they can't simply be created but must be composed with other components or species to be usable. 

 h3. Defining the Ecosystem and the Species 

 Create a SpeADL file named _banking.speadl_ in the package _tutorial2.banking_ and define a _Banking_ ecosystem in _tutorial2.banking_ namespace: 
 <pre> 
 import tutorial2.banking.interfaces.IAccountOperations 
 import tutorial2.banking.interfaces.IAccountStatus 
 import tutorial2.logging.interfaces.ILog 

 namespace tutorial2.banking { 
	
	 ecosystem Banking { 
		
		 requires elog: ILog 
		
		 species Account(owner: String) { 
			
			 provides operations: IAccountOperations 
			 provides status: IAccountStatus 
			
			 requires log: ILog 
		 } 
	 } 
 } 
 </pre> 

 Then the interfaces in _tutorial2.banking.interfaces_: 
 <pre> 
 package tutorial2.banking.interfaces; 

 public interface IAccountOperations { 

	 public void deposit(int value); 
	 public void withdraw(int value); 
 } 

 public interface IAccountStatus { 

	 public int getBalance(); 
 } 
 </pre> 

 h3. Implementing the Ecosystem and the Species 

 We now    can define an implementation named _BankingImpl_ in the package _tutorial2.banking.impl_. 
 It exploits the provided ports and the required ports of the species, but also the required port of the ecosystem: 
 <pre> 
 package tutorial2.banking.impl; 

 import java.util.concurrent.atomic.AtomicInteger; 

 import tutorial2.banking.Banking; 
 import tutorial2.banking.interfaces.IAccountOperations; 
 import tutorial2.banking.interfaces.IAccountStatus; 

 public class BankingImpl extends Banking { 
	
	 @Override 
	 protected Account make_Account(final String owner) { 
		 return new Account() { 
			
			 private final AtomicInteger balance = new AtomicInteger(); 
			
			 @Override 
			 protected void start() { 
				 eco_requires().elog().addLine("Added a new account for "+owner); 
			 } 
			
			 @Override 
			 protected IAccountOperations make_operations() { 
				 return new IAccountOperations() { 
					
					 @Override 
					 public void withdraw(int value) { 
						 provides().operations().deposit(-value); 
						 requires().log().addLine(value+" were withdrawn, current balance: "+provides().status().getBalance()); 
					 } 
					
					 @Override 
					 public void deposit(int value) { 
						 balance.addAndGet(value); 
						 requires().log().addLine(value+" were deposited, current balance: "+provides().status().getBalance()); 
					 } 
				 }; 
			 } 

			 @Override 
			 protected IAccountStatus make_status() { 
				 return new IAccountStatus() { 
					 @Override 
					 public int getBalance() { 
						 return balance.get(); 
					 } 
				 }; 
			 } 
		 }; 
	 } 
 } 
 </pre> 

 Notice the calls to *provides()*, *requires()* and *eco_requires()* from within the species.