Marinkovic • Freier Software Entwickler • Lead Developer bei Porsche Informatik • Berater bei Smarter Ecommerce • Peter Niederwieser • Senior Developer bei Smarter Ecommerce • Groovy Committer • Autor von Spock Framework
Tapestry IoC • Tapestry IoC Basics • Tapestry IoC in der Praxis • Porsche Informatik (POI) • Smarter Ecommerce (Smec) • Zusammenfassung • Beyond Tapestry IoC
(I) • Java Konfiguration • Kein XML • Typsicherheit • Annotationen • Modul Konzept • Basis für hochmodulare Anwendungen • Schrittweise Erweiterung von Komponenten • Convention over Configuration • Jedes Modul sieht gleich aus
(II) • Contributions • Ähnliches Konzept wie Extension-Points und Extensions in Eclipse • Drop-In Jars • manifest.mf enthält Tapestry-Modules Eintrag mit Module Klasse • Bei Startup werden angegebene Klassen zur Registry hinzugefügt
– Background • Sub-Projekt des Tapestry 5 Web Frameworks • Open-source / Apache Software License 2.0 – http://tapestry.apache.org/tapestry5.1/tapestry-ioc/ • Autor Howard Lewis Ship • APIs inspiriert von Google Guice
– Modules (II) • Beim Startup werden Methoden des Moduls analysiert • build prefix definiert ein Service • contribute prefix erweitert ein Service • advise prefix für AOP • public void static bind(ServiceBinder binder)
– Services • Es gibt mehrere Möglichkeiten, Services zu definieren • Via build Methode • Via bind Methode public class BinderServiceModule { public static void bind(ServiceBinder binder) { binder.bind(ServiceA.class, ServiceAImpl.class); binder.bind(ServiceB.class); } } public class ServiceModule { public ServiceA buildServiceA() { return new ServiceAImpl(); } public ServiceB buildServiceB() { return new ServiceB(); } } Optionale Service Id binder.bind(ServiceB.class).withId("ServiceB"); Implementierung muss nicht angegeben werden, falls im gleichen Package
– Injection (I) • Mehrere Möglichkeiten zur Service Injection • Typbasierte Injection – Via builder Method Konvention • Konstruktor von SecondImpl erwartet Objekt von Typ First public class InjectionModule { public First buildFirst() { return new FirstImpl(); } public Second buildSecond(First first) { return new SecondImpl(first); } } – Via automatic Injection • Analyse der Service Konstruktoren public class AutomaticInjectionModule { public static void bind(ServiceBinder binder) { binder.bind(First.class, FirstImpl.class); binder.bind(Second.class, SecondImpl.class); } }
– Injection (II) • Namensbasierte Injection • Auch auf builder Methode anwendbar public class SecondImpl implements Second { private final First first; public SecondImpl(@InjectService("betterFirst") First first) { this.first = first; } } • Module Injection • Oft benötigte Services können direkt ins Modul injected werden public class ModuleInjectionModule { private final First first; public ModuleInjectionModule(First first) { this.first = first; } public Second buildSecond() { return new SecondImpl(first); }
– Injection (III) • Marker Annotation = Refactoring Safe (Guice) • Jeder Service kann durch eine eigene Annotation spezifiziert werden • Auch auf builder Methoden anwendbar public class MarkerInjectionModule { public static void bind(ServiceBinder binder) { binder.bind(First.class, FirstImpl.class).withId("first"); binder.bind(First.class, BetterFirst.class) .withId("better").withMarker(Better.class); binder.bind(Second.class, SecondImpl.class); } } public class SecondImpl implements Second { private final First first; public SecondImpl(@Better First first) { this.first = first; } }
– Contributions (I) • Verteilte Konfiguration über Contributions • Ein Service kann aus versch. Modulen Contributions erhalten public class StringSourceImpl implements StringSource { private final Collection<String> contrib; public StringSourceImpl(Collection<String> contrib) { this.contrib = contrib; } public Collection<String> getStrings() { return contrib; } } public class ContributionModule { public static void bind(ServiceBinder binder) { binder.bind(StringSource.class, StringSourceImpl.class); } public void contributeStringSource(Configuration<String> contrib) { contrib.add("tapestry ioc"); contrib.add("spring"); contrib.add("guice"); }
– Service Lifecycles (I) • Alle Services sind per default Singletons • Eine Instanz pro Container • Alle Services sind per default lazy • Werden erst beim ersten Zugriff instantiiert • Jeder Service wird durch einen Proxy repräsentiert – Wie in Spring <aop:scoped-proxy/> – Daher Injection zwischen verschiedenen Scopes uneingeschränkt möglich • Können als eager definiert werden → Initialisierung beim Startup – @EagerLoad Annotation – ServiceBinder.bind().eagerLoad()
– Service Lifecycles (II) • PerThread-Lifecycle • Für jeden neuen Thread wir der Service neu instantiiert – In Spring abbildbar mit <aop:scoped-proxy/> • Injection wie bei Singleton Services • Nur bei Services mit Interface • Deklariert via – Annotation auf builder Methode – Annotation auf Implementierung – bind Aufruf @Scope(ScopeConstants.PERTHREAD) public class ThreadedImpl implements Threaded {}
– Chain of Command Builder • ChainBuilder • Erstellt aus einer Contribution eine Chain-of-Command public class ChainModule { public Command buildCommand(List<Command> commands, ChainBuilder builder) { return builder.build(Command.class, commands); } public void contributeCommand(OrderedConfiguration<Command> commands) { commands.addInstance("blue", BlueCommand.class, "after:*"); commands.addInstance("red", RedCommand.class, "before:blue"); commands.add("green", new GreenCommand(), "before:*"); } } public class Executor { private final Command command; public Executor(Command command) { this.command = command; } public void doSomething() { command.process(); } }
– Strategy Builder • StrategyBuilder • Generiert aus einer Contribution eine Strategy für versch. Typen public interface Printer<T> { void print(T value); } public class StrategyModule { public Printer buildPrinter(Map<Class, Printer> contrib, StrategyBuilder builder) { return builder.build(Printer.class, contrib); } public void contributePrinter(MappedConfiguration<Class, Printer> contrib) { contrib.add(String.class, new StringPrinter()); contrib.add(BigDecimal.class, new BigDecimalPrinter()); } • PipelineBuilder
Smarter Ecommerce (IV) • Modulstruktur • Published area – öffentlich zugänglich • Module area - Moduldefinition • Internal area – modul-interne Klassen • Zugriffsregeln • Published A → Published B OK • Internal A → Published B OK • Published A → Internal B Fehler • Überprüfung mit Classycle (http://classycle.sourceforge.net/)
Smarter Ecommerce (VI) • Konfiguration • Verwendung in Service • Automatische Typ-Konvertierung public class PersistenceServiceImpl implements PersistenceService { public PersistenceServiceImpl( @Symbol(PersistenceConfigSymbols.TABLE_NAME) String tableName, @Symbol(PersistenceConfigSymbols.TIMEOUT_MILLIS) long timeoutMillis) { // initialize service } }
Smarter Ecommerce (VII) • Service mit Contributions am Beispiel EventBroker public class EventModule { public static EventBrokerService buildEventBrokerService( Collection<EventReceiver> receivers) { EventBrokerServiceImpl service = new EventBrokerServiceImpl(receivers); service.start(); return service; } } public class IndexingModule { public static IndexingService build(...) { ... } public static void contributeEventBrokerService( Configuration<EventReceiver> receivers, IndexingService indexingService) { receivers.add(indexingService); } }
der Porsche Informatik (II) • Modul Struktur (ca. 70 modules) organization web logistic web valuation web invoice web ... ... Fachliche Frontend Module organization logistic valuation invoice ... ... Fachliche Backend Module 1:1 Mapping Common Supporting Modules ui admin persistence security rules scheduler
der Porsche Informatik (III) • Modul Architektur Web Backend Facade Repository DTO Facade Repository Domain Domain Service Service Service Factory Factory Persistence Module Tx Boundary Persistence Query