injection-based resource configuration (candi)
Resin's configuration uses a powerful, general dependency injection system (CanDI) to configure servlets, Resin resources, databases, application components, third-party libraries and drivers. By understanding a few rules matching the XML to the configured resources, an administrator can have full control over the application server behavior. Because of the importance of verifying and debugging configuration, Resin's configuration is stateless, meaning that the XML files fully describe the configuration of the system. Resin avoids hidden state by avoiding deployment tools and internal, hidden database configuration.
Because Resin's configuration (CanDI) creates and updates Java objects, each XML tag exactly matches either a Java class or a Java property. For security and rewrite rules, the JavaDoc helps document the configuration. Your own application's configuration can use its JavaDoc to describe the XML tags, and Resin's configuration files like the META-INF/resin-web.xml to customize your application.
Services, drivers, and third-party libraries are registered and configured using the standard Resin configuration files resin.xml or resin-web.xml files as well as the META-INF/beans.xml. Application services and libraryes are treated as first-class components just like Resin-internal resources, because Resin's own configuration uses the same CanDI configuration. Even standard JavaEE configuration like servlets, JSP .tld files, and EJBs are configured with CanDI. The configuration in Resin is smaller than some other dependency frameworks because only components which need customization need to be in the XML. If your application is using Java Injection internally, most of the wiring occurs automatically through Java code annotations instead of being configured in XML. The annotation focus makes the Java code self-describing, and also simplifies the management by shrinking the needed XML. The XML-configuration lets you customize your application for a
particular environment, e.g. setting configuration parameters. For example,
Resin's In addition, the XML-configuration documents the services you've enabled. For heavyweight services, this documentation is critical, while lightweight components do not need this extra housekeeping overhead. Component overview<web-app xmlns="http://caucho.com/ns/resin" xmlns:mypkg="urn:java:com.mycom.mypkg"> <mypkg:MyBean> [class annotations, service, and registration] [bean constructor args] [property configuration] [method annotation configuration] </mypkg:MyBean> </web-app>
The <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:ee="urn:java:ee" xmlns:example="urn:java:example"> <example:MyService> <ee:ApplicationScoped/> </example:MyService> <example:MyComponent> </example:MyComponent> </beans> The
Some services and components need a name because they're used as a JSP or JSF reference, or because the configuration needs a reference to the component. Resin configuration files can use EL expressions to get references to resources, beans, system properties, and calculate generatal expressions based on those values. Because all Resin's resources are added to the CanDI registry automatically, application components have access to anything they need. Both the JSP immediate syntax and deferred syntax are
supported ( <web-app xmlns="http://caucho.com/ns/resin" xmlns:ee="urn:java:ee"> xmlns:qa="urn:java:qa"> <qa:FooBean> <ee:Named>a</ee:Named> <qa:bar>#{b}</qa:bar> </qa:FooBean> <qa:FooBean> <ee:Named>b</ee:Named> <qa:bar>#{a}</qa:bar> </qa:FooBean> </web-app> You can also use beans as factories in the EL expressions, because
Resin's EL implementation allows method expressions. If the bean's
create method is named Resin's Java Injection configuration uses the standard JavaBeans patterns to configure properties. Resin uses the same mechanism for all of its own configuration parsing, including every JavaEE configuration file, the resin-web.xml and the resin.xml itself. So your application will have all the configuration flexibility it needs. Since the component beans can use Java
Injections, injected components are typically not configured in
the resin-web.conf, avoiding the need for tags like package example; public class Hello { public void setGreeting(String greeting); } The basic example sets a <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:example="urn:java:example"> <example:Hello> <example:greeting>Hello, World</example:greeting> </example:Hello> </beans> primitive conversionsResin automatically converts XML values to the Java property types
for most primitive values. For other primitive types, it also supports
the JavaBeans
compound typesFull sub-bean configuration is also available when a service needs a more complicated configuration than primitives allow. The service class can add sub-beans as properties and the sub-beans themselves are configured recursively using Resin's configuration. Since all JavaEE configuration files like the web.xml and the *.tld files are configured using Resin's recursive sub-bean configuration, your application has full access to a powerful recursive configuration. A sophisticated application can use Resin's sub-bean configuration to create a full domain-specific language, allowing extensive user control of the application. custom sub-beans<web-app xmlns="http://caucho.com/ns/resin"> <example:Theater xmlns:example="urn:java:example"> <example:name>Balboa</example:name> <example:movie title="The Princess Bride"/> <example:movie title="The Maltese Falcon"/> </example:Theater> </web-app> In this example, the public class Theater { public void setName(String name); public void addMovie(Movie movie) } public class Movie { public void setTitle(String title); } listSetters taking a List items are specified directly with <value> elements. There is no extra <list> element required. The <list> element is only used when creating a sub-list or sub-element (see below.) <my-bean> <values> <value>a</value> <value>b</value> <value>c</value> </values> </my-bean> <my-bean> <values> <value>a</value> <value>b</value> <value>c</value> </values> </my-bean> In the following example, the argument is an object, so we need
a <list> element to tell Resin to create a list. The object
created will be an <my-bean> <values> <list> <value>a</value> <value>b</value> <value>c</value> </list> </values> </my-bean> Resin can always use the <my-bean> <value>a</value> <value>b</value> <value>c</value> </my-bean> Some beans require constructor configuration because the service or
library designer prefers constructors to method configuration.
Resin's configuration can support these constructor beans with the
<web-app xmlns="http://caucho.com/ns/resin" xmlns:example="urn:java:example"> <example:MyBean> <new> <value>first arg<value> <value>second arg<value> </new> </example:MyBean> </web-app> public class MyBean { public MyBean(String a, String b) { ... } } Single constructorAs a convenience, Resin provides short form of the constructor configuration if there's only a single argument. You can either omit the <value> and just use the <new> tag, or you can even omit the <new> tag entirely. <web-app xmlns="http://caucho.com/ns/resin" xmlns:example="urn:java:example"> <example:MyBean> <new>single arg</new> </example:MyBean> </web-app> <web-app xmlns="http://caucho.com/ns/resin" xmlns:example="urn:java:example"> <example:MyBean>single arg<example:MyBean> </web-app> valueOfFor classes which implement a static public class MyBean { ... public static MyBean valueOf(String text) { MyBean bean = new MyBean(); bean.setTextValue(text); bean.init(); return bean; } } setValueFor objects with a
Some of your application's beans will be configured as custom services, like Java servlets or Hessian services, using CanDI to configure a service annotation. These custom services combine your own Java code with Resin capabilities, usually to expose them as external web services.
Resin is designed around the Java Contexts and Dependency Injection specification (Java CanDI, JSR-299), an inversion-of-control framework used for all configuration and resources including servlets, EJBs, messaging, remoting, and databases. Applications can take advantage of Java Injection using standard annotations and interfaces. Since Resin-CanDI is used for servlets, Java objects and EJBs, any application bean can use EJB annotations like @TransactionAttribute or CanDI @InterceptionTypes or event @Observes capabilities, in addition to the dependency injection and IoC configuration. The dependency injection framework is type-safe, meaning the registry is organized around Java types, not a flat namespace, which gives more power and flexibility for component assembly. Since injection is annotation-based, most components can avoid XML configuration, while XML is still available for components. Resin's Java Injection support is integrated
with EJB 3.1 Lite and the core components like Servlets, Filters and remote objects.
This integration means plain Java beans can use EJB annotations
and interception, EJBs can use Java Injection annotations, and both kinds of
beans can be configured directly from the So it's best to think of Java Injection as a set of orthogonal capabilities that are available to any registered bean. The basic capability types are:
You can register your components and services with Resin using the resin.xml or resin-web.xml files as well as the META-INF/beans.xml and WEB-INF/beans.xml. Since the Java Injection registry is integrated with Resin, your services be treated as first-class components along with the Resin resources. Although most components will not need XML, there are a few advantages for the small number of services which do use XML. The XML-configuration lets you customize your application for a
particular environment, e.g. setting configuration parameters. For example,
Resin's In addition, the XML-configuration documents the services you've enabled. For heavyweight services, this documentation is critical, while lightweight components do not need this extra housekeeping overhead. bean and component registrationThe <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:example="urn:java:example"> <example:MyService> <ee:ApplicationScoped/> </example:MyService> <example:MyComponent> </example:MyComponent> </beans> The
Bean property configurationResin's Java Injection configuration uses the standard JavaBeans patterns to configure properties. Resin uses the same mechanism for all of its own configuration parsing, including every JavaEE configuration file, the resin-web.xml and the resin.xml itself. So your application will have all the configuration flexibility it needs. Since the component beans can use Java
Injections, injected components are typically not configured in
the resin-web.conf, avoiding the need for tags like package example; public class Hello { private String _greeting = "default"; public void setGreeting(String greeting) { _greeting = greeting; } public String getGreeting() { return _greeting; } } The basic example sets a <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:example="urn:java:example"> <example:Hello> <example:greeting>Hello, World</example:greeting> </example:Hello> </beans> Resin's configuration uses 5 basic bean patterns, extending the JavaBeans
conventions. It can configure literal values like string and integers as
well as configuring other beans. Any component bean configured by Resin
has full access to (Currently the patterns are name-based like JavaBeans, since Resin was designed before annotations. We may add configuration annotations in the future. public void setFoo(String data); public void setFoo(Movie data); public void addFoo(Movie data); public Movie createFoo(); public void setText(String data);
As mentioned above, Resin uses these 5 patterns to handle all of the
JavaEE configuration files. In particular, the <web-app xmlns="http://caucho.com/ns/resin"> <example:Theater xmlns:example="urn:java:example"> <example:name>Balboa</example:name> <example:movie title="The Princess Bride"/> <example:movie title="The Maltese Falcon"/> </example:Theater> </web-app> In this example, the public class Theater { String _name; ArrayList<Movie> _movies = new ArrayList<Movie>(); public void setName(String name) { _name = name; } public Movie createMovie() { return new Movie(this); } public void addMovie(Movie movie) { _movies.add(movie); } public static class Movie { private Theater _theater; private String _title; Movie(Theater theater) { _theater = theater; } public void setTitle(String title) { _title = title; } } } Base configuration: string conversionsJava Injection provides a number of built-in string conversion types as well
as supporting JavaBeans
String constructorResin-CanDI will automatically convert a string to an object if the object has a single String argument constructor. public class MyBean { public MyBean(String value) { ... } } Compound typeslistSetters taking a List items are specified directly with <value> elements. There is no extra <list> element required. The <list> element is only used when creating a sub-list or sub-element (see below.) <my-bean> <values> <value>a</value> <value>b</value> <value>c</value> </values> </my-bean> <my-bean> <values> <value>a</value> <value>b</value> <value>c</value> </values> </my-bean> In the following example, the argument is an object, so we need
a <list> element to tell Resin to create a list. The object
created will be an <my-bean> <values> <list> <value>a</value> <value>b</value> <value>c</value> </list> </values> </my-bean> Resin-CanDI can always use the <my-bean> <value>a</value> <value>b</value> <value>c</value> </my-bean> References and EL ExpressionsResin-CanDI configuration files can use EL expressions to get references to resources, beans, system properties, and calculate generatal expressions based on those values. Since all Resin's resources are added to the WebBeans registry automatically, application components have access to anything they need. Both the JSP immediate syntax and deferred syntax are supported (${...} vs #{...}). Currently, there is no distinction between the two, but the deferred syntax is preferred, since Resin-IoC initializes beans lazily to handle circular references. <web-app xmlns="http://caucho.com/ns/resin" xmlns:ee="urn:java:ee" xmlns:qa="urn:java:qa"> <qa:FooBean ee:Named="a"> <bar>#{b}</bar> </qa:FooBean> <qa:BarBean ee:Named="b"> <foo>#{a}</foo> </qa:BarBean> </web-app> Because Resin's EL implementation allows method expressions, you can use beans as factories in the EL expressions. @EJB requests the injection of an EJB session bean. @Target({TYPE, METHOD, FIELD}) public @interface EJB { String name() default ""; Class beanInterface() default Object.class; String beanName() default ""; String mappedName() default ""; } @PersistenceContext requests the injection of a JPA PersistenceContext. @PersistenceContext(unitName="test") EntityManager _em; package javax.persistence; @Target({TYPE, METHOD, FIELD}) public @interface PersistenceUnit { String name() default ""; String unitName() default ""; PersistenceContextType type() default TRANSACTION; PersistenceProperty[] properties() default {}; } @PersistenceUnit requests the injection of a JPA PersistenceUnit. @PersistenceUnit(unitName="test") EntityManagerFactory _emf; package javax.persistence; @Target({TYPE, METHOD, FIELD}) public @interface PersistenceUnit { String name() default ""; String unitName() default ""; } javax.annotation.PostConstruct tells the assembler to call a method after the bean has been built, but before it is active. package javax.annotation; @Target(value={PACKAGE,TYPE}) public @interface PostConstruct { } javax.annotation.PreDestroy tells the container to call the annotated method before it is destroyed. package javax.annotation; @Target(value={PACKAGE,TYPE}) public @interface PreDestroy { } @Resource tells the assembler to retrieve a resource an assign it to a field or property. Typically, the resource will be stored in JNDI.
package javax.annotation; @Target({TYPE, METHOD, FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Resource { String name() default ""; Class<?> type() default Object.class; AuthenticationType authenticationType() default CONTAINER; boolean shareable() default true; String mappedName() default ""; String description() default ""; } Resin configures beans using bean-style (setter injection) patterns, supporting the Inversion-of-Control design pattern. A "bean" is any plain-old Java object which follows standard configuration patterns. Because Resin can find the bean-style setters from looking at the class, it can configure those setters in a configuration file like the web.xml. Resin's configuration follows the Assembly Line or Dependency Injection pattern. The Assembly Line pattern gives configuration responsibility to the container where it belongs, while keeping the application code independent of the container. Bean-style configuration setters for simple properties form the foundation for the Assembly Line pattern. If an application follows the bean patterns, it can be configuration in any container following the Assembly Line (setter injection) pattern. We strongly recommend following the Assembly Line pattern throughout an application, even if your application does not use Resin to configure itself. Following the Assembly Line pattern makes application code easier to understand, maintain, configure and test. The bean configuration form the foundation of the Assembly Line pattern. Since most applications already follow the bean patterns, they get property configuration with no changes. Each configuration parameter <init> <greeting>Hello, World!</greeting> <another-greeting>Hello, Mom!</another-greeting> </init> public class MyBean { private String _greeting; private String _anotherGreeting; public void setGreeting(String greeting) { _greeting = greeting; } public void setAnotherGreeting(String anotherGreeting) { _anotherGreeting = anotherGreeting; } } Type conversionA setter can have a parameter that has a type other than
<init> <host>www.gryffindor.com</host> <port>80</port> </init> public class MyBean { private String _host; private int _port; public void setHost(String host) { _host = host; } public void setPort(int port) { _port = port; } } CompatibilityProperty configuration is very portable. Any serious configuration system will configure bean-style properties. Setter injection connects resources following the same bean-style setter pattern. Where bean properties configure simple values like strings and integers, setter injection configures other resources like databases and application components. Resin uses JNDI to store the intermediate resources, e.g. storing a database in java:comp/env/jdbc/test. The configuration file specifies the JNDI resource using the JSP expression language and jndi. <init> <data-source>\${jndi("jdbc/test")}<data-source> </init> public class MyBean { private DataSource _dataSource; public void setDataSource(DataSource ds) { _dataSource = ds; } } Resources often act as containers for lists of values and map values.
The A setter method <init> <greeting>Hello, World!</greeting> <greeting>Hello, Mom!</greeting> </init> public class MyBean { private LinkedList _greetings = new LinkedList(); public void addGreeting(String greeting) { _greetings.add(greeting); } } Well-written resources will validate their configuration and may perform additional assembly tasks. Resin calls methods marked with the @PostConstruct annotation after all the setter methods have been called. import javax.annotation.PostConstruct; public class MyBean { private String _language; private String _country; Locale locale; public void setLanguage(String language) { _language = language; } public void setCountry(int country) { _country = country; } @PostConstruct public void init() { locale = new Locale(language, country); } } Validation ExceptionsIf an exception is thrown from any of the methods in the bean, Resin will attach a file name and line number that correspond to the configuration file. import java.util.Locale; import javax.annotation.PostConstruct; public class MyBean { private String _language; private String _country; Locale _locale; public void setLanguage(String language) throws Exception { if (language.length() != 2) throw new Exception("'language' must be a two-character string"); _language = language; } public void setCountry(int country) throws Exception { if (country.length() != 2) throw new Exception("'country' must be a two-character string"); _country = country; } @PostConstruct public void init() { if (_country == null) throw new Exception("'country' is required"); if (_language == null) throw new Exception("'language' is required"); _locale = new Locale(language,country); } } 500 Servlet Exception WEB-INF/web.xml:9: java.lang.Exception: 'country' must be a two-character string Beans can be nested, allowing a bean to have setters that have other sub-beans as the type. <init> <table> <name>Foo</name> <timestamp-field>tstamp</timestamp-field> </table> <table name="Bar" timestamp-field="ts"/> </init> import javax.annotation.PostConstruct; import javax.sql.*; // a class to periodically clean old log records from the database public class LogCleaner { List _logTables = new LinkedList(); // the createXXX method is optional, and allows use something other than // the default constructor for a sub-bean public LogTable createTable() { return new LogTable(); } // you could also use setTable(LogTable logTable) public void addTable(LogTable logTable) { _logTables.add(logTable); } public class LogTable { String _name; String _timestampField; public void setName(String name) { _name = name; } public void setTimestampField(String timestampField) { _timestampField = timestampField; } @PostConstruct public void init() throws Exception { if (_name == null) throw new Exception("'name' is required"); if (_timestampField == null) throw new Exception("'timestamp-field' is required"); } public void cleanTable(DataSource pool) { Connection conn = null; try { conn = pool.getConnection(); ... } catch (SQLException e) { throw new ServletException(e); } finally { try { if (conn != null) conn.close(); } catch (SQLException e) { } } } } ... } The <init> <message>This is the message</message> </init> public class MyBean { Message _msg; public Message createMessage() { return new Message(); } public void setMessage(Message msg) { _msg = msg; } public class Message { String _text; public void addText(String text) { _text = text; } public String getText() { return _text; } } } There are some unusual cases where the configured bean is just a
configuration object and you want to return a different bean. The
bean can implement a method <beans xmlns="http://caucho.com/ns/resin" xmlns:resin="urn:java:com.caucho.resin" xmlns:example="urn:java:com.foo.example"> <example:MessageBean resin:JndiName="env/message"> <message>This is message 1</message> <message>This is message 2</message> <message> <example:CustomMessage/> </message> </example:MessageBean> </beans> package example; import java.util.*; public class MessageBean { List _msgs = new LinkedList(); public void addMessage(Message msg) { _msgs.add(msg); System.out.println("MessageBean.addMessage(): " + msg); } // this never get's called, because MessageBean has no parent public void setParent(Object obj) { System.out.println("MessageBean.setParent(): " + obj); } } package example; public class Message { String _text; public void addText(String text) { _text = text; System.out.println("Message.addText(): " + text); } public void setParent(Object obj) { System.out.println("Message.setParent(): " + obj); } public String toString() { return super.toString() + ": " + _text; } } package example; public class CustomMessage extends Message { public void addText(String text) { _text = text; System.out.println("CustomMessage.addText(): " + text); } public void setParent(Object obj) { System.out.println("CustomMessage.setParent(): " + obj); } } Message.setParent(): example.MessageBean@ffb35e Message.addText(): This is message 1 MessageBean.addMessage(): example.Message@1591b4d: This is message 1 Message.setParent(): example.MessageBean@ffb35e Message.addText(): This is message 2 MessageBean.addMessage(): example.Message@10f965e: This is message 2 CustomMessage.setParent(): example.MessageBean@ffb35e CustomMessage.addText(): This is message 3 MessageBean.addMessage(): example.CustomMessage@12164ea: This is message 3 In practice, it may make more sense to use The facilities provided by Resin make it very easy to read XML files. Java classes that follow the java bean pattern are defined to match the schema of the xml file. <rss version="0.91"> <channel> <title>Hogwarts</title> <link>http://hogwarts.com</link> <description>Hogwart's News</description> <image> <title>Hogwarts</title> <url>http://hogwarts.com/images/logo.gif</url> <link>http://hogwarts.com</link> <width>88</width> <height>31</height> <description>Hogwart's News</description> </image> <item> <title>New Course Additions</title> <link>http://hogwarts.com/news/00123.html</link> <description>New course's are now available at Hogwart's.</description> </item> <item> <title>Dumbledore is back!</title> <link>http://hogwarts.com/news/00122.html</link> <description> After a short hiatus, Professor Dumbledore is back as Headmaster of Hogwart's. </description> </item> </channel> </rss> package example.rss; import java.util.ArrayList; public class Rss { private String _version; private ArrayList<Channel> _channels = new ArrayList<Channel>; public void setVersion(String version) { _version = version; } public String getVersion() { return _version; } public void addChannel(Channel channel) { _channels.add(channel); } public ArrayList<Channel> getChannels() { return _channels; } } package example.rss; import java.util.ArrayList; public class Channel { private String _title; private String _link; private String _description; private String _language; private Image _image; private ArrayList<Item> _items = new ArrayList<Item>; public void setTitle(String title) { _title = title; } public String getTitle() { return _title; } public void setLink(String link) { _link = link; } public String getLink() { return _link; } public void setDescription(String description) { _description = description; } public String getDescription() { return _description; } public void setImage(Image image) { _image = image; } public Image getImage() { return _image; } public void addItem(Item item) { _items.add(item); } public ArrayList<Items> getItems() { return _items; } } package example.rss; public class Image { private String _title; private String _url; private String _link; private int _width; private String _height; private String _description; public void setTitle(String title) { _title = title; } public String getTitle() { return _title; } public void setLink(String link) { _link = link; } public String getLink() { return _link; } public void setWidth(int width) { _width = width; } public int getWidth() { return _width; } public void setHeigth(int height) { _height = height; } public int getHeight() { return _height; } public void setDescription(String description) { _description = description; } public String getDescription() { return _description; } } package example.rss; public class Item { private String _title; private String _link; private String _description; public void setTitle(String title) { _title = title; } public String getTitle() { return _title; } public void setLink(String link) { _link = link; } public String getLink() { return _link; } public void setDescription(String description) { _description = description; } public String getDescription() { return _description; } } NodeBuildercom.caucho.config.NodeBuilder is used to configure beans from an xml file. import com.caucho.config.NodeBuilder; import com.caucho.vfs.Path; ... Rss rss = new Rss(); NodeBuilder builder = new NodeBuilder(); Path rssPath = new Path("WEB-INF/rss-example.xml"); builder.configure(rss,rssPath); RNC validationimport com.caucho.config.NodeBuilder; import com.caucho.vfs.Path; ... Rss rss = new Rss(); NodeBuilder builder = new NodeBuilder(); Path rssPath = new Path("WEB-INF/rss-example.xml"); Path schemaPath = new Path("WEB-INF/rss.rnc")builder.setCompactSchema(schemaPath); builder.configure(rss,rssPath); resin:importresin:import is used to read configuration information from another file. <rss version="0.91"> <resin:import> <fileset dir="channels/"> <include name="*.xml"/> </fileset> </resin:import> <channel> <title>Hogwarts</title> <link>http://hogwarts.com</link> <description>Hogwart's News</description> <image> <title>Hogwarts</title> <url>http://hogwarts.com/images/logo.gif</url> <link>http://hogwarts.com</link> <width>88</width> <height>31</height> <description>Hogwart's News</description> </image> <item> <title>New Course Additions</title> <link>http://hogwarts.com/news/00123.html</link> <description>New course's are now available at Hogwart's.</description> </item> <item> <title>Dumbledore is back!</title> <link>http://hogwarts.com/news/00122.html</link> <description> After a short hiatus, Professor Dumbledore is back as Headmaster of Hogwart's. </description> </item> </channel>
|