360 likes | 380 Views
Introduction to Component Models and Technologies. Why do we need them ? What do they minimally do ? How do they actually do it ?. Review: Component Models and Component Frameworks.
E N D
Introduction to Component Models and Technologies Why do we need them ? What do they minimally do ? How do they actually do it ?
Review: Component Models and Component Frameworks Platform Services: allow components written according to the model to communicate; locating, linking, replacing components Component Component Component Component platform (component framework) Middleware Operating System Horizontal Services: application-independent services used by different components. Concurrency, security, transaction management, Resource management Hardware
Component technologies • Component technology = component model + component framework • Different component models available: • Old and new • Industrial or research • General-purpose or specialized for different domains • Having different concepts for components • Providing a larger or smaller set of platform services • Examples: • Java Beans, EJB, COM, DCOM, .NET Components, CCM, OSGI, Spring, PicoContainer, Fractal, OpenCOM, Autosar, KOALA, PECOS, …
Introduction goals • Motivation • Why do we need them ? • What is the minimum they must do ? • Basic principles • How do they actually do it ? • Bibliography: Martin Fowler: Inversion of Control Containers and the Dependency Injection Pattern, http://www.martinfowler.com/articles/injection.html
The Basic Goals of CBD • Assemble a system out of existing (third-party) components • Update a system by adding / replacing components • Component: • Is a unit of deployment • Is handled as it is (a blackbox)
SpellChecker1 TextEditor SpellChecker2 Example 1 • A simple text editor can be composed with a spell checker for English language or with a spell checker for Romanian language
Example 1 • The Component Diagram shows a simple hierarchical composition
MovieFinder1 MovieLister MovieFinder2 Example 2 • From: Martin Fowler: Inversion of Control Containers and the Dependency Injection Pattern
Example 2 • A MovieLister is able to list movies with certain characteristics after being provided an exhaustive list of movies by a MovieFinder • MovieFinder is an interface; • There could be several different MovieFinderImpl components: one implementation finds movies by reading a text file, one finds movies from a relational database, one finds movies crawling the web for movie advertisings, etc.
public interface MovieFinder { List findAll(); } public class MovieLister { private MovieFinder finder; public Movie[] moviesDirectedBy(String arg) { List allMovies = finder.findAll(); for (Iterator it = allMovies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); if (!movie.getDirector().equals(arg)) it.remove(); } return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]); } } This is good BUT the MovieLister will still need a MovieFinderImplementation !
public interface MovieFinder { List findAll(); } public class MovieLister { private MovieFinder finder; public Movie[] moviesDirectedBy(String arg) { List allMovies = finder.findAll(); for (Iterator it = allMovies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); if (!movie.getDirector().equals(arg)) it.remove(); } return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]); } public MovieLister() { finder = new MySpecialMovieFinderImplem(); } } This is BAD !!
The goal: loosely coupled components • MovieLister should work with any MovieFinderImplementation • MovieLister does not need to know the particular type of finder implementation it is using • The good solution: eliminate all lines of code such as: • MovieFinder f = new MyParticularMovieFinderImpl(); • A component should NEVER create (instantiate) its dependencies • The solution is called “Inversion of Control” • In this context, IoC means that a component does not create (instantiate) its dependencies but has someone else creating them for it
The concept of “Inversion of Control” • The general definition: • All application frameworks make use of a design pattern known as “inversion of control” • It occurs whenever we define code that will be called by the framework to handle application specific behavior • This is what distinguishes a framework from a library
The concept of “Inversion of Control” • In the context of components: • The application independently defines a set of components and their dependencies and the component framework (called container) uses this information to • wire the components together at run-time • call its code at specific times in the life cycle
Inversion of Control • Inversion of Control can be achieved through several patterns: • Dependency Injection • Service Locator
Dependency Injection An Assembler instantiates concrete implementations and “injects” them into the component that needs them
Forms of Dependency Injection • Constructor Injection • MovieLister has a constructor that will get the MovieFinderImplementation • Setter Injection • MovieLister has a setter method that will get the MovieFinderImplementation • Interface Injection • An interface InjectFinder, with method injectFinder, defined by the provider of the MovieFinder interface • MovieLister (and any class that wants to use a MovieFinder) needs to implement this interface
Component Containers • What has the Dependency Injector pattern to do with component frameworks ? • The “Assembler” component of the Dependency Injection pattern is called a “Component Container” and is part of the component framework • The assembler (Container) is generic (for any application), thus it: • Requires that components follow a certain convention (constructor, setter, injector interfaces) • Requires to be told (by code or configuration files) which implementation to associate with which interface
Using Component Containers • Take a set of components (concrete classes + interfaces) • they have to implement constructors, setters or interfaces for dependency injection – according to the convention required by the component framework • Add configuration info in form of: • Configuration metadata (config files) • Configuration code • Add client code to use the container
Examples • Component Containers are used in many component frameworks: • “Lightweight” IoC Containers • Complex component frameworks, that provide also other features beyond IoC Containers • Examples: • PicoContainer • Spring • Unity • Guice • EJB • CCM • OSGI with DS
Example: Constructor Injection with PicoContainer (1) Including Constructors class MovieLister... public MovieLister(MovieFinder finder) { this.finder = finder; } Each class declares constructors that include everything it needs injected class ColonMovieFinder... public ColonMovieFinder(String filename) { this.filename = filename; }
Example: Constructor Injection with PicoContainer (2) Describing the configuration private MutablePicoContainer configureContainer() { MutablePicoContainer pico = new DefaultPicoContainer(); Parameter[] finderParams = {new ConstantParameter("movies1.txt")}; pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams); pico.registerComponentImplementation(MovieLister.class); return pico; } The pico container needs to be told which implementation class to associate with each interface, and which string to inject into the finder This kind of configuration information could come as well from configuration files. Some component frameworks support reading it from config files
Example: Constructor Injection with PicoContainer (3) Using the composed system public void testWithPico() { MutablePicoContainer pico = configureContainer(); MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }
Example: Setter Injection with Spring (1) Defining setters class MovieLister... public void setFinder(MovieFinder finder) { this.finder = finder; } Each class defines setters that include everything it needs injected class ColonMovieFinder... public void setFilename(String filename) { this.filename = filename; }
Example: Setter Injection with Spring (2) Describing the configuration <beans> <bean id="MovieLister" class="spring.MovieLister"> <property name="finder"> <ref local="MovieFinder"/> </property> </bean> <bean id="MovieFinder" class="spring.ColonMovieFinder"> <property name="filename"> <value>movies1.txt</value> </property> </bean> </beans>
Example: Setter Injection with Spring (3) Describing the configuration public void testWithSpring() throws Exception { ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml"); MovieLister lister = (MovieLister) ctx.getBean("MovieLister"); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }
Inversion of Control • Inversion of Control can be achieved through several patterns: • Dependency Injection • Service Locator
Service Locator A Service Locator knows how to get hold of all of the services that an application might need.
Example: Service Location with OSGI (1) Registering the service public class BasicMovieFinderActivator implements BundleActivator { private ServiceRegistration registration; public void start(BundleContext context) { MovieFinder finder = new BasicMovieFinderImpl(); registration = context.registerService( MovieFinder.class.getName(), finder, null); …. } This Service Locator is a Dynamic one (it allows to stash any service you need into it and make the choices at runtime)
Example: Service Location with OSGI (2) Retrieving the service public class MovieListerActivator implements BundleActivator { private ServiceTracker finderTracker; public void start(BundleContext context) throws Exception { … finderTracker = new ServiceTracker(context, MovieFinder.class.getName(), null); finderTracker.open(); MovieFinder finder = (MovieFinder) finderTrack.getService(); …. }
Comparison: Service Locator vs Dependency Injection • Both provide decoupling (keep application code dependent only on interfaces, not on implementations) • Other criteria: • How explicit are the dependencies ? • How “intrusive” is the approach into the application code ? • Dynamic dependencies are possible ?
Comparison: (1)Service Locator vs Dependency Injection • “Intrusive” into application code: • With service locator, the application component explicitly asks the locator for its dependency • Every component has a dependency to the locator • This is “intrusive” into the component development process (such components cannot be used without the framework) • With dependency injection, the application component does not have to ask anything, its dependencies just appear injected • Less intrusive, such components could be used without the framework
Comparison: (2)Service Locator vs Dependency Injection • Explicit dependencies: • With service locator you have to search the source code for calls to the locator. • With dependency injection you can just look at the injection mechanism, such as the constructor, and see the dependencies.
Comparison: (3)Service Locator vs Dependency Injection • Dynamic dependencies: • With dependency injection you don't have a dependency from a component to the injector, the component cannot obtain further services from the injector once it's been configured. • With service locator a component can locate new services at any time or replace/update them .
Conclusion • Component frameworks must support the assembly of independent components into applications at deployment time or even runtime • lightweight IoC containers at least • Component frameworks may support different additional aspects: • Component lifecycle support • Hierarchical components • Concurrency, distribution, transactions, …