Definition/Scope:
Initially when Switchboard was being prototyped, the responsibility of Switchboard was to provide a populated Enivornment Naming Context (ENC) for Java EE component(s). Later when Switchboard got integrated with various Java EE technologies in AS6 (like servlets, EJBs etc...) it was realized that Switchboard would also have a role to play, in providing an invokable ENC.
So ultimately, Switchboard is responsible for providing a fully populated as well as a invokable (i.e. the objects bound in that JNDI context should be usable via lookup) ENC. A fully populated and an invokable ENC will be provided by Switchboard during different "stages" of deployment. More details about this is made available in the rest of the section of this document.
The details:
That one line definition, above, will be more clear once we understand the details and the rationale behind Switchboard and jboss-injection projects.
Java EE components like Servlets, EJBs have a java:comp/env JNDI context where component specific resources are made available. Consider some of the following examples:
public class MyServlet extends HttpServlet { @EJB (name="echoBean") private Echo someSLSB; ... }
The above example shows a Servlet which injects a EJB of type "Echo" into the field "someSLSB". Apart from the injection part, the other important part in that code is the @EJB(name="echoBean") annotation itself. The spec mandates that such resources should be made available in the java:comp/env namespace of the component (the Servlet in this case). Effectively, the above code does the following 2 things:
1. Makes available the EJB, of type Echo, at java:comp/env/echoBean jndi name in the java:comp/env of MyServlet.
2. (Internally) looks up the EJB from java:comp/env/echoBean jndi name and injects the returned object into the "someSLSB" field.
Let's consider another similar piece of code, this time within an EJB:
@Stateless public class MyBean { @PersistenceContext(name="myPC") private EntityManager em; ... }
Similar to the Servlet example, here we have an EJB "MyBean" which expects a PersistenceContext to be injected in the "em" field. Like in the Servlet case, the following 2 things are expected to done by the server runtime:
1. The PersistenceContext/EntityManager is expected to be made available at java:comp/env/myPC jndi name, in the java:comp/env namespace of the MyBean.
2. (Internally) looks up the PersistenceContext from java:comp/env/myPC jndi name and injects the returned object into "em" field.
So, as can be seen in these examples, there are 2 common steps involved in the injection process and applies across all Java EE components. That's where the "Switchboard" and the "jboss-injection" projects come into picture. To put in simple terms, "Switchboard" is expected to be responsible for the #1 step and "jboss-injection" is expected to be responsible for the #2 step, shown above.
In addition to the above example, there are other ways where a user deployment can expect the resources to be available in java:comp/env namespace. They can do it through the use of deployment descriptors:
<?xml version='1.0' ?> <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0"> <enterprise-beans> <session> <ejb-name>Stateless3Bean</ejb-name> <ejb-ref> <ejb-ref-name>ejb/Stateless2</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> </ejb-ref> </session> </enterprise-beans> </ejb-jar>
In this case the ejb-ref is expected to be made available at java:comp/env/ejb/Stateless2 in the java:comp/env namespace of Stateless3Bean. This configuration doesn't configure any injection point(s). So the "jboss-injection" part won't come into picture here. It's just the "switchboard" part which is responsible for setting up the java:comp/env.
In the above examples, we have seen the use of ejb-ref, @EJB and @PersistenceContext. The resource references aren't limited to these. There are a lot many different types of references that can be made available in the java:comp/env namespace of the component. For a complete set of such types of references, take a look at the chapter "EE.5 Resources, Naming and Injection" of JavaEE6 spec.
The working details:
As we have outlined above, it's mainly a 2 step process to inject into a Java EE component. The first step is Switchboard's reponsibility (which internally is again a 2 step process - remember, (1) a fully populated ENC and (2) a invokable ENC) and the second is the responsiblity of jboss-injection (Note that we keep repeating this expectation so that we have clear understanding of what purpose Switchboard and what jboss-injection serves). Let's take a deeper look into how things work. Since this document is mainly about Switchboard details, we will focus mainly on Switchboard. But wherever necessary we will explain a bit of jboss-injection.
A typical Java EE component (like an EJB) is backed by a "container" within a server runtime. The "container" is responsible for instantiating the component (example: the EJB) and injecting the resources in the instantiated instance. Ofcourse there are other things the "container" is expected to do, but all that is out-of-scope of this document. So, once the container instantiates the component (like an EJB), it has to then inject the references into that instance. As we have seen in our earlier examples, this is exactly the step#2 that has been outlined there. i.e. the "container" has to lookup from the java:comp/env namespace of that component and then inject that retured reference into the field/property of the component instance. This effectively means that the java:comp/env namespace has to be populated with all the referenced resources before the injection can take place. This means that for the "container" to allow invocations on the component (example, the EJB) it "depends" on the piece of code (let's call it that, for simplicity) which sets up the java:comp/env of the component. That "piece of code" is the "Switchboard".
Switchboard implementation details:
Now that we understand what Switchboard is, let's move on the implementation details of that project. Let's first take a look at the EnvironmentEntryType interface:
EnvironmentEntryType
package org.jboss.switchboard.spi; /** * {@link EnvironmentEntryType} represents a resource reference that has to be made * available in a Java EE component's Environment naming context (ENC). * * * @author Jaikiran Pai * @version $Revision: $ */ public interface EnvironmentEntryType { /** * The returned name is a JNDI name which will be used to bind a {@link Resource} in the Java EE component's ENC . * <p/> * The name can have, as a prefix, either of the following spec provided namespaces: * <ul> * <li>java:global</li> * <li>java:app</li> * <li>java:module</li> * <li>java:comp</li> * </ul> * If the returned name has none of the above prefix, then it will be considered to be relative to * java:comp namespace * <p/> * The name must be unique within a Deployment Component. * * @return the jndi name of a Deployment Component's environment entry */ String getName(); }
The EnvironmentEntryType represents a resource reference that has to be made available in a Java EE component's ENC namespace. In our previous examples, the @EJB, ejb-ref and @PersistenceContext were each a EnvironmentEntryType. The getName() method in this interface is expected to return the jndi name which will be used to bind the resource reference. So considering this example:
@EJB (name="echoBean") private Echo someSLSB;
The getName() of EnvironmentEntryType is expected to return "java:comp/env/echoBean" or "env/echoBean" (since, by default, the java:comp is considered as the context).
Let's now take a look at the Resource interface
package org.jboss.switchboard.spi; * A {@link ResourceProvider} resolves a particular metadata type into a {@link Resource}. * <p> * The {@link Resource} is then picked up by the switchboard operator and made available * in the JNDI * </p> * * @author Jaikiran Pai * @version $Revision: $ */ public interface Resource { /** * Returns the dependency (for example, name of some component) which * has to be fulfilled before this {@link Resource} can be made available * in JNDI * @return * @see #getInvocationDependencies() */ Object getDependency(); /** * Returns the target object corresponding to this {@link Resource} * @return */ Object getTarget(); /** * Returns the dependencies which (for example, name of some component) have to be * fulfilled before this {@link Resource} can be looked up from JNDI and invoked upon. * <p/> * Note that, this method is different from {@link #getDependency()}. The {@link #getDependency()} * returns the dependency that has to be resolved before this Resource can be <b>bound</b> to JNDI. * A {@link Resource} bound in JNDI need <i>not</i> be immediately ready for allowing lookups and invocations. * In such cases, this method is expected to return the dependencies which have to be resolved for it to * allow lookup and invocation on the {@link Resource}. * @return */ Collection<?> getInvocationDependencies(); }
The "Resource" represents the runtime aspect of the EnvironmentEntryType. As can be seen, there are 3 important methods in a Resource. The first one:
Object getDependency();
is expected to return the "dependency" which has to be satisfied, before the Switchboard can make the "target" of this Resource, available in the ENC of the component. Note that, as the javadoc on that method says, when the dependency is resolved the "target" is just bound to the ENC. It doesn't mean, that it can be looked up or invoked upon. See the getInvocationDependencies() API for details on how to return the dependencies that have to resolved for a target to be looked up and invoked upon successfully.
The dependency management part is delegated to the runtime (for example, JBoss Microcontainer in AS6) within which the Switchboard and the other components are running. Let's consider a concrete example to understand better. Let's consider this example:
@Stateless public class MyBean { @EJB(name="otherEJB") private BeanB bean2; ... }
Let's now represent this into Switchboard constructs. So we now have a EnvironmentEntryType (which represents the @EJB) whose EnvironmentEntryType.getName() returns "env/otherEJB". This EnvironmentEntryType (metadata) will be converted to a Resource by ResourceProviders (we'll look at them later). Now, for the Resource representing this @EJB to be made available in ENC, the corresponding EJB deployment should be processed and deployed into the runtime. Effectively, the Resource depends on some component(s) which is responsible for:
1) Some component which binds the EJBs to global JNDI name (to which we can create a LinkRef). Let's call this the EJB jndi binder
2) The EJB container which can handle invocations on the bean
Until the #1 dependency on EJB jndi binder is satisfied, we can't bind the Resource to ENC. So the getDependency() method on the Resource, returned by the EJB ResourceProvider should return the jndi binder name (in JBoss Microcontainer land, it's the MC bean name of the EJB jndi binder) to depend on.
Furthermore, until the #2 dependency on EJB container is satisfied, the Resource in ENC cannot be looked up (a lookup can lead to invocation on a container) or invoked upon. So the getInvocationDependencies() method should return the name of the container (in JBoss Microcontainer land, it's the MC bean name of the EJB container) to depend upon.
Moving on to the next method in Resource interface:
Object getTarget();
This method is expected to return the "target" object that will be bound into JNDI once the dependency on the Resource is satisfied. The jndi name at which the "target" will be bound is decided by the value returned by EnvironmentEntryType.getName(). In the example above, the target will be a LinkRef to the global jndi name of the EJB.
Now, let's next take a look at the ResourceProvider interface:
package org.jboss.switchboard.spi; public interface ResourceProvider<C, T extends EnvironmentEntryType> { /** * Returns a {@link Resource} for the passed context and {@link EnvironmentEntryType} * * @param context The context * @param type The type of environment entry * @return */ Resource provide(C context, T type); }
As can be seen, the ResourceProvider processes a EnvironmentEntryType and provides a Resource for that EnvironmentEntryType. A ResourceProvider works off a "context" and a EnvironmentEntryType. Let's a take a concrete example to understand more about ResourceProvider(s).
Let's take this example:
@Stateless public class MyBean { @PersistenceContext(name="myPC") private EntityManager em; ... }
Let's now represent this into Switchboard constructs. So we now have a EnvironmentEntryType (which represents the @PersistenceContext) whose EnvironmentEntryType.getName() returns "env/myPC". Furthermore, we'll have a PersistenceContextResourceProvider (let's call it that) which might look like:
public class PersistenceContextProvider<C> implements ResourceProvider<C, PersistenceContextEnvEntryType> { public Resource provide(C context, PersistenceContextEnvEntryType type) { ... } }
The responsibility of the ResourceProvider(s) is to process the EnvironmentEntryType(s) within a "context" and return a Resource which the switchboard can use to setup the java:comp/env.
Switchboard usage:
Like we explained earlier, the "container" depends on the Switchboard to make available the ENC. Typically, the Switchboard is known by a "id" with which it is registered within a (dependency management) runtime. The "container" can then mark itself as dependent on that "id" of the switchboard.
In AS6, the Switchboard is attached to a deployment unit and can be obtained in a deployer as follows:
Barrier barrier = unit.getAttachment(Barrier.class);
The Barrier represents the Switchboard and the interface looks like this:
/** * A SwitchBoard {@link Barrier} provides a way to add dependencies * on component for a fully populated java:comp ENC {@link Context context}. * * <p> * A {@link Barrier} is identified by a unique {@link #getId() id} which can used * in a dependency management runtime to setup dependencies between a {@link Barrier} * and a dependent component. The dependent component typically depend on a fully * populated {@link Context context}. * </p> * <p> * A {@link Barrier} depends on multiple {@link Resource#getDependency() resources} provided by * various {@link ResourceProvider resource providers}. When all those dependencies are resolved * the {@link Barrier barrier} reaches a fully installed state, at which point the java:comp * {@link #getContext() context} is fully populated with environment entries * </p> * * @author Jaikiran Pai * @version $Revision: $ */ public interface Barrier { /** * Returns the id for this {@link Barrier}. * <p> * The {@link Barrier} will be deployed in a dependency management runtime and * will be identified by this unique id. Any component which wants to depend on this * Barrier can add appropriate dependencies (using dependency management runtime provided ways) * on this {@link Barrier} using this id. * </p> * @return */ String getId(); /** * Returns the naming context which represents the java:comp of a * Java EE component. * <p> * The returned {@link Context} is fully populated with environment entries * <i>only</i> after the dependencies on this {@link Barrier} have been fully * resolved. * </p> * @return */ Context getContext(); }
Java EE EnvironmentEntryTypes:
For all this to work together, the important parts that need to be implemented out of the switchboard project, are the various ResourceProviders. We need ResourceProviders for each of the following Java EE EnvironmentEntryType (all these can be found in the "javaee" module of switchboard here)
* AnnotatedEJBRefType - Represents @EJB
* EJBLocalReferenceType - Represents <ejb-local-ref>
* EJBReferenceType - Represents <ejb-ref>
* MessageDestinationRefType - Represents <message-destination-ref>
* PersistenceContextRefType - Represents @PersistenceContext and its xml equivalent <persistence-context-ref>
* PersistenceUnitRefType - Represents @PersistenceUnit and its xml equivalent <persistence-unit-ref>
* ResourceRefType - Represents @Resource and its xml equivalent <resource-ref>
* ResourceEnvRefType - Represents @Resource and its xml equivalent <resource-env-ref>
* ServiceRefType - Represents <service-ref>
* SimpleEnvironmentEntryType - Represents @Resource and <resource-ref> for simple environment entries
ResourceProvider(s) for AS6:
For AS6, the various ResourceProviders are expected to implement this interface:
package org.jboss.switchboard.mc; ... public interface MCBasedResourceProvider<T extends EnvironmentEntryType> extends ResourceProvider<DeploymentUnit, T>
As can be seen, the MCBasedResourceProvider is an extension of ResourceProvider which works on MC based DeploymentUnit. For example, the provider which processes EJBReferenceType, in AS6 will look like:
public class EJBResourceProvider implements MCBasedResourceProvider<EJBReferenceType> { public Resource provide(DeploymentUnit unit, EJBReferenceType ejbRef) { ... } }
Status:
JIRA https://jira.jboss.org/browse/JBAS-8548
As of AS 6.0.0.Final, Switchboard has been integrated into web and EJB tier, as targeted. The PersistenceContext resource provider still needs to be integrated and is currently targeted for AS 6.0.1.
Deployers:
Note that the deployers which are meant to deploy the switchboard artifacts are internal implementation details and aren't expected to be relied on by any of the ResourceProviders or jboss-injection. The implementation of the deployers for switchboard can be found here http://github.com/jaikiran/jboss-switchboard/tree/master/mc-impl/src/main/java/org/jboss/switchboard/mc/deployer/.
As explained earlier, the jboss-injection project (which pulls the resources bound in ENC and injects into the instances) is expected to use the populated javax.naming.Context provided by SwitchBoard. Each deployment unit will be have a attachment of type org.jboss.switchboard.spi.Barrier http://github.com/jaikiran/jboss-switchboard/blob/master/spi/src/main/java/org/jboss/switchboard/spi/Barrier.java. The jboss-injection deployers which want to use the SwitchBoard first have to add this as an input and then in their deploy methods, retrieve the attachment.
Source code:
The switchboard project currently is being developed at http://github.com/jaikiran/jboss-switchboard/.
Also see:
Comments