Programming Concepts
Components
In Fabric3, application code is organized as a set of components implemented as POJOs. A component may have one or more services it exposes to clients and may contain references to other services it depends on. The following illustrates a calculator component (taken from the samples) that is wired to operand services:
@Component public class CalculatorServiceImpl implements CalculatorService { @Reference protected AddService addService; @Reference protected SubtractService subtractService; @Reference protected MultiplyService multiplyService; @Reference protected DivideService divideService; public double add(double n1, double n2) { return addService.add(n1, n2); } public double subtract(double n1, double n2) { return subtractService.subtract(n1, n2); } public double multiply(double n1, double n2) { return multiplyService.multiply(n1, n2); } public double divide(double n1, double n2) { return divideService.divide(n1, n2); } }
Bindings
Components can provide one or more services to clients. Clients may be remote or local. For remote clients, a component service can be exposed as an endpoint using a binding such as WS-*, JAX-RS or JMS.
Fabric3 supports native JAX-RS annotations for binding a component service as an endpoint:
@Path("/") @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN) @Component public class CalculatorServiceImpl implements CalculatorService { @Reference protected AddService addService; @Reference protected SubtractService subtractService; @Reference protected MultiplyService multiplyService; @Reference protected DivideService divideService; @GET @Path("/{formula}") public String calculate(@PathParam("formula") String formula) { // ... } }
Fabric3 also provides annotations for binding services to other transports. The WebServiceBinding
annotation exposes the calculator service as a WS-* endpoint:
@WebServiceBinding(uri = "CalculatorService") public class CalculatorServiceImpl implements CalculatorService { // ... }
The following demonstrates how to expose an endpoint to a JMS queue:
@Component @JMS(@JMSConfiguration(destination = "Queue")) public class ConsumerImpl implements Consumer { public String onMessage(String message) { return message; } }
Wires
A component can act as a client to a service by wiring a reference.
The following shows how to wire a reference in a composite:
@Component public class CalculatorServiceImpl implements CalculatorService { @Reference @Target("AddService") protected AddService addService; // ... }
In the above example, the wire from CalculatorServiceImpl
to AddService
can be local or remote (possibly passing through a message queue) depending on if the two components are collocated. For distributed services, bindings can be specified on references:
@Component public class CalculatorServiceImpl implements CalculatorService { @Reference @ZeroMQ("AddService") protected AddService addService; // ... }
The above example wires CalculatorServiceImpl
to AddService
using a ZeroMQ socket. Note that IP addresses and port bindings do not need to be specified - these will be resolved transparently to the application by the Fabric3 runtime.
Composites
Components can be defined using annotations (in which case they will be scanned by the runtime) or by XML or a Java-based DSL. If you prefer to define components using one of the latter methods, you must create a composite containing the components. A composite can be defined by an XML file containing a set of component definitions:
<composite xmlns='http://docs.oasis-open.org/ns/opencsa/sca/200912' targetNamespace='urn:tempuri.org' name='SampleComposite'> <component name='CalculatorService'> <implementation.java class="org.sample.CalculatorServiceImpl"/> </component> </composite>
Alternatively, a composite can be defined using the Fabric3 DSL:
public class CompositeProvider { @Provides public static Composite calculatorComposite() { QName name = new QName(Namespaces.F3, "CalculatorComposite"); JavaComponentDefinitionBuilder componentBuilder = JavaComponentDefinitionBuilder.newBuilder(CalculatorServiceImpl.class); return CompositeBuilder.newBuilder(name).component(componentBuilder.build()).build(); } }
A composite is similar to a Spring application context but with several important differences:
- An application can be comprised of 1..n composites that are peers or nested in a top-level composite.
- Composites define visibility boundaries for encapsulation. It is not possible to wire to components within a composite unless they are explcitly made visibile outside the composite through promotion. Promotion is similar to making a method public on a Java class.
Channels
Fabric3 also supports pub/sub interactions where components pass events through a channel as opposed to being directly wired. A component producer is connected to a channel, which in turn may be connected to 0..N component consumers.
The following shows how to create pub/sub interaction using a channel using XML:
<composite ...> <channel name="SampleChannel"/> <component name='SampleComponent'> <implementation.java/> <producer name="channel" target="SampleChannel"/> </component> <component name='TargetComponent1'> <implementation.java/> <consumer name="channel" source="SampleChannel"/> </component> <component name='TargetComponent2'> <implementation.java/> <consumer name="channel" source="SampleChannel"/> </component> </composite>
Channels may be local or remote depending on where its producers and consumers are deployed. In the case where producers and consumers are remote, Fabric3 will bind a channel to a remote transport such as a JMS topic (channels may also be explicitly bound using the <binding> element in a composite).