Welcome to the SwitchYard User Guide. This document provides a detailed explanation of the moving parts in SwitchYard. If you are looking for a way to get up and running quickly, be sure to check out the Getting Started guide quickly.
The Bean Component is a pluggable container in SwitchYard which allows Java classes (or beans) to provide and consume services. This means that you can implement a service by simply annotating a Java class. It also means you can consume a service by injecting a reference to that service directly into your Java class. Rather than writing our own POJO container to provide this capability, we have implemented the bean component as a Weld extension. No need to learn a new programming model - bean services are standard CDI beans with a few extra annotations. This also opens up the possibilities of how SwitchYard is used; you can now expose existing CDI-based beans in your application as services to the outside world or consume services within your bean.
Providing a service with the Bean component is as simple as adding an @Service annotation to your bean.
@Service(SimpleService.class) public class SimpleServiceBean implements SimpleService { public String sayHello(String message) { System.out.println("*** Hello message received: " + message); return "Hi there!!"; } } public interface SimpleService { String sayHello(String message); }
The SimpleService interface represents the Service Interface, defining the service operations that are exposed by SwitchYard.
The only other thing you need is a META-INF/beans.xml file in your deployed application. When the application is deployed, the Weld runtime scans the application for beans and enlists the SwitchYardCDIServiceDiscovery CDI extension in the CDI lifecycle. Our extension will pick up @Service beans and make them available to the application deployer (will depend on the container). At this point, the service can be invoked from other services within SwitchYard or bound to a wire protocol via SwitchYard gateways.
All SwitchYard Services, no matter what the implementation type, are composed of one or more Service Operations . As you might imagine, in the case of a Bean Service, the Service Operations are the set of Java methods exposed by the Service Interface.
There are a few restrictions when it comes to defining Bean Service Operations:
Declares exactly one Input type i.e. the Java method signature must have a maximum of one Java parameter.
Declares a maximum of one Output type. This is obviously enforced by the Java language since you can only define one return type on a Java method.
Declares a maximum of one Fault (Exception) type.
All Service Operations on a SwitchYard Service can define an Input , Output and Fault message. These messages have a type associated with them, which is defined as a QName. This type is used by the data transformation layer, when trying to work out which transformers to apply to a Message payload.
For bean Services, the default type QName for Input (input param), Output (return value) and Fault (Exception) are derived from the Java class name in each case (param, return, throws). For some types however (e.g. org.w3c.dom.Element ), the Java type name alone does not tell you the real type of the data being held by that Java Object instance. For this reason, Bean Service Operations (methods) can be annotated with the @OperationTypes annotation e.g.
public interface OrderService { @OperationTypes( in = "{http://acme.com/orders}createOrder", out = "{http://acme.com/orders}createOrderResult", fault = "java:com.acme.exceptions.OrderManagementException" ) Element createOrder(Element order) throws OrderCreateFailureException; }
It's also possible to set the default data type for a Java type using the @DefaultType Type level annotation. This is useful for setting the type for a hierarchy of Java types. Note in the example code above how we changed the type for OrderCreateFailureException (to "java:com.acme.exceptions.OrderManagementException") by defining a fault type on the @OperationTypes . It's type would otherwise default to "java:com.acme.exceptions.OrderCreateFailureException". We could also do this by annotating the base OrderManagementException class with the @DefaultType annotation. This would set the default type for the OrderManagementException class and all its sub-classes, including OrderCreateFailureException , which would mean not having to defining a fault type on the @OperationTypes wherever one of these exceptions is used on a Bean Service Operation.
Sometimes you need access to the SwitchYard Exchange
Context
instance associated with a given Bean Service Operation invocation. To access this, simply add a
Context
property to your bean and annotate it with the CDI
@Inject
annotation.
@Service(SimpleService.class) public class SimpleServiceBean implements SimpleService { @Inject private Context context; public String sayHello(String message) { System.out.println("*** Funky Context Property Value: " + context.getPropertyValue("funkyContextProperty")); return "Hi there!!"; } }
Note that you can only make calls on the
Context
instance within the scope of one of the Service Operation methods. Invoking it outside this scope will result in an
UnsupportedOperationException
being thrown.
Consuming a SwitchYard service from within a CDI bean is done via @Reference annotations.
@Service(ConsumerService.class) public class ConsumerServiceBean implements ConsumerService { @Reference private SimpleService service; public void consumeSomeService(String consumerName) { service.sayHello("Hello " + consumerName); } } public interface ConsumerService { void consumeSomeService(String consumerName); }
Note that the contract for the service is all that's exposed to the bean consumer. The reference could point to a service that is hosted outside of SwitchYard and exposed over JMS, SOAP, FTP, etc. The SwitchYard runtime handles the resolution of the service reference to a concrete service, allowing your service logic to remain blissfully ignorant. Invocations made through this reference are routed through the SwitchYard exchange mechanism.
The @Reference annotation can accept a service name in cases where the default name resolution (interface Class simple name e.g. "OrderService") are not appropriate.
@Reference("urn:myservices:purchasing:OrderService") private OrderService orders;
This can be useful when the default name resolution is not appropriate. Keep in mind that the name used by the reference is not required to match the target service, but it resolve to a service through some mechanism. For example, the deployed application could contain wiring configuration which maps the service name used by the bean reference to a different service name in the runtime.
Unit testing in SwitchYard is very easy. See the Testing SwitchYard Services documentation.
Easiest way to get started is to create a new application using the SwitchYard Forge Tooling and switchyard.bean facet.
As of SwitchYard 0.2.0, applications can be deployed in a JEE Web Application Container such as Tomcat or Jetty i.e. you don't need to use the SwitchYard Application Servers (AS6 or AS7).
Two distinct tasks need to be performed in order to get your CDI based SwitchYard application working in your Web Application Container:
Configure the
SwitchYard
WebApplicationDeployer
Servlet Listener in your Web Application
.
Configure Weld into your Servlet Container .
See the "webapp-deploy" quickstart (in the demos folder) as an example of how to create a CDI Bean based SwitchYard application that can be deployed on Tomcat.
The JavaServer Faces (JSF) technology provides a server-side component framework that is designed to simplify the development of user interfaces (UIs) for Java EE applications. JSF has very tight integration with CDI to provide the Object Model behind the JSF user interface components.
The fact that SwitchYard Bean Services are based on CDI means it's possible to have a really nice integration between SwitchYard Bean Services and a JSF based user interface. This section is not an CDI or JSF reference, but does provide some tips/guidelines on using these technologies within the context of SwitchYard. This list of tips will grow and be refined as more use cases are tested and verified.
The following guidelines/tips should apply to any container when building a JSF user interface on top of a SwitchYard Service.
As with any JSF based user interface, the JSF pages will contain EL tokens referencing scoped CDI beans (by name ala the
@Named
bean annotation). It's possible to annotate your SwitchYard Service CDI beans with the
@Named
annotation and inject them directly into your application's JSF components, but that would result in your JSF pages making direct invocations on the Service implementation (i.e. the Service implementation bean would be injected directly into the JSF components). What you really want to do is to invoke your SwitchYard Services through the SwitchYard
Exchange
mechanism. This reduces the coupling between your JSF components and your SwitchYard Service implementations.
Currently, the only way to invoke a SwitchYard CDI Bean Service through the SwitchYard
Exchange
mechanism is to invoke them through a
@Reference
injected Service reference (other options may be available in future). This provides a client side proxy bean that handles all the SwitchYard
Exchange
invocation magic for all the operations exposed by the Service in question. The catch here is that these proxy beans are not available (through CDI) to the JSF components, so you need a bog standard named (
@Named
) CDI bean containing an injected
@Reference
sitting between the JSF components and the SwitchYard CDI Bean Services. This is not really a problem though, because your JSF components will likely have a more natural interaction with a business/model type value-object bean (getters/setters) than they will with a Service interface type bean (operations). So, a layer of indirection will probably make sense anyway.
So if you consider an example of an
OrderService
(Service implementation not shown):
public interface OrderService { OrderAck submitOrder(Order order); } public class Order implements Serializable { private String orderId; private String itemId; private int quantity = 1; public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } }
Your JSF components will probably have a more natural interaction with the
Order
bean than with the
OrderService
e.g.:
<div id="content"> <h1>New Order</h1> <div style="color: red"> <h:messages id="messages" globalOnly="false" /> </div> <h:form id="newOrder"> <div> Order ID: <h:inputText id="orderID" value="#{order.orderId}" required="true"/> <br/> Item ID: <h:inputText id="itemID" value="#{order.itemId}" required="true"/> <br/> Quantity: <h:inputText id="quantity" value="#{order.quantity}" required="true"/> <p/> <h:commandButton id="createOrder" value="Create" action="#{order.create}"/> </div> </h:form> </div>
However, in order to make this work, we need to make a few tweaks to the
Order
bean:
Annotating it with
@Named
and
@RequestScoped
.
Adding a
@Reference
to the
OrderService
.
Implementing the
create
method that invokes the
OrderService
reference (
Exchange
proxy).
@Named @RequestScoped public class Order implements Serializable { @Inject @Reference private OrderService orderService; private String orderId; private String itemId; private int quantity = 1; public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } public void create() { OrderAck serviceAck = orderService.submitOrder(this); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(serviceAck.toString())); } }
To reiterate, because of the
@Reference
annotation, the
orderService
property instance is not a reference to the actual Service implementation. Instead, it is a SwitchYard
Exchange
proxy to that Service implementation.
Also note that by using
@Reference
injected Service references, your backend Service implementation can be a non CDI Bean Service implementation e.g. a Camel Routing Services. This mechanism opens up all sorts of integration possibilities!
See the "orders" quickstart (in the demos folder) as an example of how to provide a JSF user interface on top of a SwitchYard CDI Bean Service deployed on SwitchYard AS7.
Camel services allow you to leverage the core routing engine inside of Apache Camel to route between services in SwitchYard. The route itself is exposed as a service within SwitchYard, which means it has a well-defined contract and can be injected into any other service in the runtime. The routing logic can be expressed in XML and included directly in the service component configuration, or you can use the Java DSL to define the route in an annotated Java class.
SwitchYard provides an
@Route
annotation which can be used to declare that a class contains a Camel route which should be represented as a service in SwitchYard. The @Route annotation has a single required element which identifies the service interface to be used by the route. Aside from this annotation, the class looks exactly like a route that you would define in Camel alone.
@Route(JavaDSL.class) public class JavaDSLBuilder extends RouteBuilder { public void configure() { from("switchyard://JavaDSL") .log("Message received in Java DSL Route") .log("${body}") .split(body(String.class).tokenize("\n")) .filter(body(String.class).startsWith("sally:")) .to("switchyard://XMLService?operationName=acceptMessage"); } }
When an application containing one or more Java DSL routes is built, the SwitchYard Maven plugin will automatically generate the required service component configuration in
META-INF/switchyard.xml
. The above route would produce the following configuration:
<component name="JavaDSLBuilder"> <implementation.camel xmlns="urn:switchyard-component-camel:config:1.0"> <java class="org.switchyard.quickstarts.camel.service.JavaDSLBuilder"/> </implementation.camel> <service name="JavaDSL"> <interface.java interface="org.switchyard.quickstarts.camel.service.JavaDSL"/> </service> </component>
The "from" endpoint in a Camel service must always equal the service name. This allows SwitchYard to establish service bindings outside the route definition itself.
Configuring a Camel service using XML is done using the <implementation.camel> configuration directly as follows:
<sca:component name="CamelComponent"> <implementation.camel xmlns="urn:switchyard-component-camel:config:1.0"> <route xmlns="http://camel.apache.org/schema/spring" id="Camel Test Route"> <log message="ItemId [${body}]"/> <to uri="switchyard://WarehouseService?operationName=hasItem"/> <log message="Title Name [${body}]"/> </route> </implementation.camel> <sca:service name="OrderService"> <sca:interface.java interface="org.switchyard.component.camel.deploy.support.OrderService"/> </sca:service> <sca:reference name="WarehouseService"> <sca:interface.java interface="org.switchyard.component.camel.deploy.support.WarehouseService"/> </sca:reference> </sca:component>
You'll notice in the above configuration that the route in the <implementation.camel> does not specify a from endpoint on the route definition. For Camel XML routes, Switchyard automatically adds this for the service. The above would be invokable by using the following code (snippet from a SwitchYard test):
String title = newInvoker("OrderService").operation("getTitleForItem").sendInOut("10").getContent(String.class);
Running the above code snippet would generate the following in you console log:
10:57:45,915 INFO [impl.DefaultCamelContext] Apache Camel 2.6.0 (CamelContext: camel-1) started in 0.838 seconds 10:57:46,284 INFO [impl.DefaultCamelContext] Route: Camel Test Route started and consuming from: Endpoint[switchyard://OrderService] 10:57:46,307 INFO [impl.DefaultCamelContext] Apache Camel 2.6.0 (CamelContext: camel-1) is starting 10:57:46,307 INFO [impl.DefaultCamelContext] Total 1 routes, of which 1 is started. 10:57:46,307 INFO [impl.DefaultCamelContext] Apache Camel 2.6.0 (CamelContext: camel-1) started in 0.000 seconds 10:57:46,428 INFO [Camel Test Route] ItemId [10] 10:57:46,434 INFO [Camel Test Route] Title Name [Fletch]
The Camel component contains not only wraps Camel, it defines a Camel component itself which maps to
switchyard://
endpoint URIs. As seen in the above examples, the
switchyard://
endpoint scheme can be used to route between Camel routes and SwitchYard services. Endpoint configuration is very straightforward:
switchyard://[service-name]?operationName=[operation-name]
service-name
: name of the SwitchYard service
operation-name
: name of the service operation to be invoked. This is only used on references and is optional if the target service only has a single operation.
SwitchYard integrates the CDI Bean
Registry
with the SwitchYard Camel components. This means that, within SwitchYard Camel Route definitions, you can reference named (@Named) CDI Beans.
Consider an example of where you have the following CDI bean:
@Named @ApplicationScoped public class StringUtil { public String trim(String string) { return string.trim(); } // Other utilities... }
This bean can be used inside your SwitchYard Camel Routes as follows:
@Route(JavaDSL.class) public class JavaDSLBuilder extends RouteBuilder { public void configure() { from("switchyard://JavaDSL") .split(body(String.class).tokenize("\n")) .filter(body(String.class).startsWith("sally:")) .bean(StringUtil.class, "trim(String)"); } }
See Camel's Bean Binding documentation for more details.
The BPM Component is a pluggable container in SwitchYard which allows a business process to be exposed as a service. One fronts their business process with a custom interface and, if desired, can easily annotate it's methods to define which should start a process, signal a process event, or abort a process.
To provide a service with the BPM component, you will have to:
Define your process using BPMN2.
Create a java interface, fronting your BPMN2 process, which can be exposed to other services, and/or to your binding(s).
Add the component containing the implementation and service interface to the SwitchYard configuration. This step can be automated using annotations and the SwitchYard plugin, explained below.
Here is an example of the component section of the SwitchYard configuration:
<component name="MyService"> <implementation.bpm xmlns="urn:switchyard-component-bpm:config:1.0" processDefinition="META-INF/MyService.bpmn" processDefinitionType="BPMN2" processId="MyService"> <taskHandler class="org.switchyard.component.bpm.task.SwitchYardServiceTaskHandler" name="SwitchYard Service"/> </implementation.bpm> <service name="MyService"> <interface.java interface="org.switchyard.userguide.MyService"/> </service> </component>
The MyService interface can be as simple as this, with no SwitchYard-specific imports:
package org.switchyard.userguide; public interface MyService { public void start(String data); }
However, if you want to have the SwitchYard configuration for this section auto-generated for you, you can use annotations and the SwitchYard Maven Plugin. To do this, you can either add SwitchYard-specific annotations to your interface directly, or (and this is the recommended approach) you can create a separate interface for this purpose. Let's do that:
package org.switchyard.userguide; import org.switchyard.component.bpm.Process; @Process(MyService.class) public interface MyServiceProcess {}
That's all you need to create the SwitchYard configuration you saw above! The values for the processDefinition, processDefinitionType and processId are all defaulted based on the name of the Process interface (the "value" to the Process annotation), however you can override these if you desire:
package org.switchyard.userguide; import org.switchyard.component.bpm.Process; @Process(value=MyService.class, definition="META-INF/MyService.bpmn", definitionType="BPMN2", id="MyService", resources={}, taskHandlers={}) public interface MyServiceProcess {}
At this point you might be wondering what the resources and taskHandlers attributes of the Process annotation are, and how did the SwitchYardServiceTaskHandler is that got added automatically in the SwitchYard configuration above.
First, a Resource represents an additional artifact that is required by your BPM process. It could be anything you could think of, including a properties file, a Drools Rule Language file, or whatever. But it needs to be available to the BPM component's runtime for that process. You can either add the resource to the XML yourself, which would like like this:
<implementation.bpm ...> <resource xmlns="urn:switchyard-config:switchyard:1.0" location="/META-INF/MyServiceRules.drl" type="DRL"/> </implementation.bpm>
, or you can add a class which represents the Resource, and reference that class in the resources attribute of the Process annotation, like this:
package org.switchyard.userguide; import org.switchyard.common.io.resource.SimpleResource; import org.switchyard.component.bpm.Process; @Process(value=MyService.class, resources={MyServiceRules.class}) public interface MyServiceProcess { public static final class MyServiceRules extends SimpleResource { public MyServiceRules() { super("/META-INF/MyServiceRules.drl", "DRL"); } } }
A note about Resources: In SwitchYard 0.3, it is expected that the resources attribute of the Process annotation will change from a Class array to a String array, and the resource type (eg: "DRL") will be deduced from the file extension. This will make the annotated class a lot smaller. You can track the progress of this change via jira issue SWITCHYARD-428 .
Next, a TaskHandler is a way for you to add your own code into the business process. Simply implement the
org.switchyard.component.bpm.task.TaskHandler
interface, and add it to the SwitchYard configuration, either manually in XML, or by referencing your implementation via the taskHandlers attribute of the Process annotation, similarly to how we did with Resources above. A BaseTaskHandler abstract class is available for you to extend, implementing all the boilerplate code, leaving you to just plug in your business logic.
By default, the SwitchYardServiceTaskHandler is always added for you by the SwitchYard Maven plugin. That handler allows you to easily call out to other SwitchYard services by name within your business process. Please see the section "Consuming a BPM Service" below for more information.
In addition to the interface-level Process annotation, there are also three method-level annotations available to you. Here is an example:
package org.switchyard.userguide; public interface MyService { public void start(String data); public void signal(String data); public void stop(String data); } package org.switchyard.userguide; import org.switchyard.component.bpm.AbortProcessInstance; import org.switchyard.component.bpm.Process; import org.switchyard.component.bpm.SignalEvent; import org.switchyard.component.bpm.StartProcess; @Process(MyService.class) public interface MyServiceProcess extends MyService { @StartProcess @Override public void start(String data); @SignalEvent("myEvent") @Override public void signal(String data); @AbortProcessInstance @Override public void stop(String data); }
This will then create the following SwitchYard configuration (note that the default processAction type is
START_PROCESS
for a method, which is why in our original version of this example we didn't have to use the @StartProcess annotation):
<component name="MyService"> <implementation.bpm xmlns="urn:switchyard-component-bpm:config:1.0" processDefinition="META-INF/MyService.bpmn" processDefinitionType="BPMN2" processId="MyService"> <processAction name="start" type="START_PROCESS"/> <processAction eventType="myEvent" name="signal" type="SIGNAL_EVENT"/> <processAction name="stop" type="ABORT_PROCESS_INSTANCE"/> <taskHandler class="org.switchyard.component.bpm.task.SwitchYardServiceTaskHandler" name="SwitchYard Service"/> </implementation.bpm> <service name="MyService"> <interface.java interface="org.switchyard.userguide.MyService"/> </service> </component>
There are two ways of consuming Services with the SwitchYard BPM component. The first way is to invoking the BPM implementation through a gateway binding, and the second is invoking other SwitchYard Services from within a BPM process itself.
Since the BPM component exposes a java interface fronting the business process, you can use any of the bindings provided by SwitchYard. This could be a SOAP Binding or a Camel Binding , for example. Please refer to those sections of this guide for more information.
To invoke a SwitchYard Service from inside a BPM process, we use the SwitchYardServiceTaskHandler (provided out-of-the-box) which we described above. To make authoring BPMN2 processes easier, SwitchYard provides a new widget for the jBPM 5 Eclipse visual editor palette. Here is a screenshot from the "Help Desk" demo quickstart:
On the left hand side under "Service Tasks", you can see the SwitchYard Service widget. On the right hand side, you can see the various points of the business process where SwitchYard Services are being invoked. Once you have dropped a SwitchYard Service task in the main window, you can customize it via the Eclipse Properties Editor. The two properties you will care about most are ServiceName and ServiceOperationName . In our example, we might have a different Service called "AnotherService", with a method called "doSomething". To invoke that operation of that service, we would configure those properties respectively. That's all there is to it!
You can read a more detailed explanation of the Help Desk quickstart demo, as well as how to set up Eclipse to make use of new widget, on this wiki page .
Unit testing in SwitchYard is very easy. See the Testing SwitchYard Services documentation.
Easiest way to get started is to create a new application using the SwitchYard Forge Tooling and switchyard.bpm facet.
The Rules Component is a pluggable container in SwitchYard which allows business rules to be exposed as a service. One fronts their rules with a custom interface and, if desired, can easily annotate it's methods to define which should execute the rules.
To provide a service with the Rules component, you will have to:
Define your rules. The Rules Component currently supports Drools as the rule engine. Even though it is quite simple to write rules in Drools , that project's developer tooling and business analyst tooling are very mature.
Create a java interface, fronting your rules, which can be exposed to other services, and/or to your binding(s).
Add the component containing the implementation and service interface to the SwitchYard configuration. This step can be automated using annotations and the SwitchYard plugin, explained below.
Here is an example of the component section of the SwitchYard configuration:
<component name="MyService"> <implementation.rules xmlns="urn:switchyard-component-rules:config:1.0" stateful="false"> <rulesAction name="process" type="EXECUTE_RULES"/> <resource xmlns="urn:switchyard-config:switchyard:1.0" location="/org/switchyard/userguide/MyService.drl" type="DRL"/> </implementation.rules> <service name="MyService"> <interface.java interface="org.switchyard.userguide.MyService"/> </service> </component>
The MyService interface can be as simple as this, with no SwitchYard-specific imports:
package org.switchyard.userguide; public interface MyService { public void process(MyData data); }
However, if you want to have the SwitchYard configuration for this section auto-generated for you, you can use annotations and the SwitchYard Maven Plugin. To do this, you can either add SwitchYard-specific annotations to your interface directly, or (and this is the recommended approach) you can create a separate interface for this purpose. Let's do that:
package org.switchyard.userguide; import org.switchyard.component.rules.ExecuteRules; import org.switchyard.component.rules.Rules; @Rules(value=MyService.class, stateful="false", resources={MyServiceDrl.class}) public interface MyServiceRules extends MyService { @ExecuteRules @Override public void process(MyData data); public static final class MyServiceDrl extends SimpleResource { public MyServiceDrl() { super("/org/switchyard/userguide/MyService.drl", "DRL"); } } }
That will create the SwitchYard configuration you saw above!
A few important points:
In the original switchyard xml, the <rulesAction/> element is optional. If not specified, the behavior is to execute the rules. In the future, more rulesActions will be supported.
You will have to configure the switchyard maven plugin with the
RulesSwitchYardScanner
.
The "stateful" attribute is not necessary. It defaults to "false" (stateless). Please visit the Drools documentation to learn more about stateless vs. stateful knowledge sessions .
You can list as many resources as the Drools knowledge session requires.
You don't have to define the drl in java. You can create a switchyard.xml file with the drl defined (as shown in the first example), and the plugin will merge what it found via annotations with what you provided in a preliminary switchyard.xml file.
A note about Resources: In SwitchYard 0.3, it is expected that the resources attribute of the Rules annotation will change from a Class array to a String array, and the resource type (eg: "DRL") will be deduced from the file extension. This will make the annotated class a lot smaller. You can track the progress of this change via jira issue SWITCHYARD-428 .
If
stateful="false"
, then each invocation of your service method will create a new Drools knowledge session, execute it given the passed-in domain data, and then be cleanly disposed.
However, if
stateful="true"
, then the intent is for the stateful knowledge session to stick around spanning multiple invocations. The way you can continue a stateful session is to include the
RulesConstants.CONTINUE
variable in your SwitchYard Exchange context properties. To dispose of a stateful session, include the
RulesConstants.DISPOSE
variable instead. (Note: if your binding is SOAP, then the SOAP headers come across as Exchange context properties.)
Sometimes it is helpful to audit the Drools rules execution (eg: for debugging). You can add the following to your switchyard.xml file to accomplish this:
<implementation.rules ...> <rulesAudit interval="1000" log="event" type="THREADED_FILE"/> </implementation.rules>
Without the above line, no auditing will be done. If you only have a
<rulesAudit/>
element, auditing will be done with the default values shown in the above example. You can of course change these values as you see fit. The
interval
is how many milliseconds Drools waits in-between audit sampling, the
log
names a log file (dependent on
type
), and
type
defines how the auditing is outputted. Valid values are
CONSOLE
,
FILE
and
THREADED_FILE
.
Since the Rules component exposes a java interface fronting the rules, you can use any of the bindings provided by SwitchYard. This could be a SOAP Binding or a Camel Binding , for example. Please refer to those sections of this guide for more information.
Unit testing in SwitchYard is very easy. See the Testing SwitchYard Services documentation.
Easiest way to get started is to create a new application using the SwitchYard Forge Tooling and switchyard.rules facet.
Camel binding support in SwitchYard allows Camel components to be used as gateway bindings for services and references within an application.
NOTE: The primary focus of Camel integration in 0.1 was the use of the core EIP routing engine within Camel as a service implementation. Testing for Camel bindings was limited to the Camel File component in 0.1, but this will be expanded considerably in 0.2. If you run into an issue with a specific Camel component in SwitchYard, please let us know in the SwitchYard forum.
Composite-level services can be bound to a Camel component using the
<binding.camel>
binding definition. The following configuration options are available for
binding.camel
:
configURI
: contains the Camel endpoint URI used to configure a Camel component instance
operationSelector
: specification of the operation name to use for the message exchange. If the target service only has a single operation, this setting is optional.
Here's an example of what a service binding looks like using a Camel component.
<sca:composite name="SimpleCamelService" targetNamespace="urn:userguide:simple-camel-service:0.1.0"> <sca:service name="SimpleCamelService" promote="SimpleComponent/SimpleCamelService"> <camel:binding.camel configURI="file://target/input?fileName=test.txt&initialDelay=50&delete=true"> <camel:operationSelector operationName="print"/> </camel:binding.camel> </sca:service> <!-- sca:component definition omitted --> </sca:composite>
Binding a reference with Camel is very similar to binding a service. The only significant difference is that specification of the operationSelector is not required on reference bindings.
<sca:composite name="orders" targetNamespace="urn:switchyard-quickstart-demo:orders:0.1.0"> <sca:reference name="WarehouseService" promote="OrderComponent/WarehouseService" multiplicity="1..1"> <camel:binding.camel configURI="file://target/output"/> </sca:reference> </sca:composite>
The SOAP component in SwitchYard provides SOAP-based web service binding support for services and references in SwitchYard.
Composite-level services can be exposed as a SOAP-based web service using the
<binding.soap>
binding definition. The following configuration options are available for
binding.soap
when binding services:
wsdl
: location of the WSDL used to describe the web service endpoint. A relative path can be used if the WSDL is included in the deployed application. If the WSDL is located outside the application, then a
file:
or
http:
URL can be used.
serverPort
: port number that the HTTP service should bind to for the SOAP endpoint. Defaults to 8080 if unspecified.
serverHost
: host name or ip address that the HTTP service should bind to for the SOAP endpoint. Defaults to localhost if unspecified.
wsdlPort
: port name in the WSDL to use. If unspecified, the first port definition in the WSDL is used for the service endpoint
contextPath
: additional context path for the SOAP endpoint. Default is none.
The default port of the SOAP binding conflicts with the default listener on JBoss AS, so you must specify a unique port in your configuration when deploying to JBoss AS. Tighter integration with JBoss WS is planned to eliminate this annoying constraint.
Here's an example of what a SOAP service binding looks like:
<sca:composite name="orders" targetNamespace="urn:switchyard-quickstart-demo:orders:0.1.0"> <sca:service name="OrderService" promote="OrderService"> <soap:binding.soap> <soap:wsdl>wsdl/OrderService.wsdl</soap:wsdl> <soap:serverPort>9000</soap:serverPort> </soap:binding.soap> </sca:service> </sca:composite>
Binding a reference with SOAP can be used to make SOAP-based web services available to SwitchYard services. The following configuration options are available for
binding.soap
when binding references:
wsdl
: location of the WSDL used to describe the web service endpoint. A relative path can be used if the WSDL is included in the deployed application. If the WSDL is located outside the application, then a
file:
or
http:
URL can be used.
wsdlPort
: port name in the WSDL to use. If unspecified, the first port definition in the WSDL is used for the service endpoint.
<sca:composite name="orders" targetNamespace="urn:switchyard-quickstart-demo:orders:0.1.0"> <sca:reference name="WarehouseService" promote="OrderComponent/WarehouseService" multiplicity="1..1"> <soap:binding.soap> <soap:wsdl>wsdl/OrderService.wsdl</soap:wsdl> </soap:binding.soap> </sca:reference> </sca:composite>
Transformation represents a change to the format and/or representation of a message's content. The representation of a message is simply the Java contract (e.g. java.lang.String, org.example.MyFancyObject) used to access the underlying content. The format of a message refers to the actual structure of the data itself. Examples of data formats include XML, JSON, CSV, and EDI.
Take the following message content:
<MyBook> <Chapter1> <Chapter2> </MyBook>
The format of this content is XML. One representation of XML in Java is as a String. Of course, the representation could also be a org.w3c.dom.Document, java.io.InputStream, etc.
String content = "<MyBook>...";
Transformation plays an important role in connecting service consumers and providers, since the format and representation of message content can be quite different between the two. For example, a SOAP gateway binding will likely use a different representation and format for messages than a service offered by a Java Bean. In order to route services from the SOAP gateway to the Bean providing the service, the format and representation of the SOAP message will need to change. Implementing the transformation logic directly in the consumer or provider pollutes the service logic and can lead to tight coupling. SwitchYard allows for the transformation logic to declared outside the service logic and injected into the mediation layer at runtime.
Transformation of message content is specified in the descriptor of your SwitchYard application (switchyard.xml). The qualified name of the type being transformed from as well as the type being transformed to are defined along with the transformer implementation. This allows transformation to be a declarative aspect of a SwitchYard application, as the runtime will automatically register and execute transfomers in the course of a message exchange.
<transforms> <transform.java class="org.switchyard.quickstarts.demos.orders.Transformers" from="{urn:switchyard-quickstart-demo:orders:1.0}submitOrder" to="java:org.switchyard.quickstarts.demos.orders.Order"/> </transforms>
There are two methods available for creating a Java-based transformer in SwitchYard:
Implement the
org.switchyard.transform.Transfomer
interface and add a
<transform.java>
definition to your switchyard.xml.
Annotate one or more methods on your Java class with
@Transformer
.
When using the
@Transformer
annotation, the SwitchYard maven plugin will automatically generate the
<transform.java>
definition(s) for you and add them to the switchyard.xml packaged in your application. The following Java class would produce the
<transform.java>
definition provided above:
public class MyTransformer { @Transformer(from = "{urn:switchyard-quickstart-demo:orders:1.0}submitOrder") public Order transform(Element from) { // handle transformation here } }
The optional
from
and
to
elements of the
@Transformer
annotation can be used to specify the qualified type name used during transformer registration. If not supplied, the full class name of the method parameter will be used as the from type and the full class name of the return type will be used as the to type.
There are three distinct transformation models available with Smooks in SwitchYard:
XML to Java : Based on a standard Smooks Java Binding configuration.
Java to XML : Based on a standard Smooks Java Binding configuration.
Smooks : This is a "normal" Smooks transformation in which the developer must define which Smooks filtering Result is to be exported back to the SwitchYard Message as the transformation result.
Smooks transformations are declared by including a
<transform.smooks>
definition in switchyard.xml.
<transform.smooks config="/smooks/OrderAck_XML.xml" from="java:org.switchyard.quickstarts.transform.smooks.OrderAck" to="{urn:switchyard-quickstart:transform-smooks:1.0}submitOrderResponse" type="JAVA2XML"/>
The
config
attribute points to a Smooks resource containing the mapping definition. The type attribute can be one of
SMOOKS
,
XML2JAVA
, or
JAVA2XML
.
The
JSON
transformer provides a basic mapping facility between POJOs and JSON (JSON marshalling and unmarshalling). Just like the
JAXB Transformer
, specification of the transformer requires a
to
and
from
specification with one Java type and one QNamed JSON type, depending on whether you're performing a Java to JSON or JSON to Java transformation.
The following configuration illustrates a JSON to Java transformation.
<trfm:transform.json from="{urn:switchyard-quickstart:transform-json:1.0}order" to="java:org.switchyard.quickstarts.transform.json.Order"/>
The following configuration illustrates a Java to JSON transformation of the same types as above.
<trfm:transform.json from="java:org.switchyard.quickstarts.transform.json.Order" to="{urn:switchyard-quickstart:transform-json:1.0}order"/>
The
XSLT
transformer allows you to perform a transformation between 2 types, using an
XSLT
. It is configured simply by specifying the
to
and
from
QNames, as well as the path to the XSLT to be applied.
<transform.xslt from="{http://acme/}A" to="{http://acme/}B" xsltFile="com/acme/xslt/A2B.xslt"/>
The
JAXB
transformer allows you to perform Java to XML (and XML to Java) transformations using
JAXB
(XML marshalling and unmarshalling). It is exactly like the
JSON Transformer
in terms of how it is configured i.e. a
to
and
from
configuration with one Java type and one QNamed XML type.
JAXB Java models can be generated from an XML Schema using XJC, or from a WSDL using tools like wsconsume (.sh/.bat), which is shipped with both SwitchYard AS6 and AS7 distros (in the bin directory).
Working out the available transformations is quite simple. Just look in the
ObjectFactory
class source file (
ObjectFactory.java
). In this source file you will see factory methods representing the available marshallings/unmarshallings e.g.
@XmlElementDecl(namespace = "http://com.acme/orders", name = "create") public JAXBElement<CreateOrder> createOrder(CreateOrder value) { return new JAXBElement<Order>(_CreateOrder_QNAME, CreateOrder.class, null, value); }
In the above example, the
@XmlElementDecl
annotation tells us that the XML QName associated with the
com.acme.orders.CreateOrder
type is "
{
http://com.acme/orders
}create
". From this, we can deduce that we have the following possible SwitchYard JAXB Transformer configurations:
<transform.jaxb from="{http://com.acme/orders}create" to="java:com.acme.orders.CreateOrder" /> <transform.jaxb from="java:com.acme.orders.CreateOrder" to="{http://com.acme/orders}create" />
One of the nice things about the JAXB Transformer integrations in SwitchYard is the fact that JAXB Transformations are automatically detected and automatically added, for your application deployment. So, if for example, you develop a CDI Bean Service and use JAXB generated types in the Service Interface, you will not need to configure any of the transformations. SwitchYard will automatically detect their availability for your application and will automatically apply them at the appropriate time during service invocation.
Each SwitchYard application must include a configuration descriptor named
switchyard.xml
in the
/META-INF
directory of it's archive. The basic structure of this descriptor is:
A parent
<switchyard>
element which contains all other configuration.
Exactly one child
<composite>
element, which contains the SCA description of the application.
Zero or one
<transforms>
elements which can contain one or more transform definitions.
Here's an example of what a SwitchYard descriptor looks like:
<switchyard xmlns="urn:switchyard-config:switchyard:1.0" xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912" xmlns:bean="urn:switchyard-component-bean:config:1.0" xmlns:soap="urn:switchyard-component-soap:config:1.0"> <sca:composite name="orders" targetNamespace="urn:switchyard-quickstart-demo:orders:0.1.0"> <sca:service name="OrderService" promote="OrderService"> <soap:binding.soap> <soap:serverPort>18001</soap:serverPort> <soap:wsdl>wsdl/OrderService.wsdl</soap:wsdl> </soap:binding.soap> </sca:service> <sca:component name="InventoryService"> <bean:implementation.bean class="org.switchyard.quickstarts.demos.orders.InventoryServiceBean"/> <sca:service name="InventoryService"> <sca:interface.java interface="org.switchyard.quickstarts.demos.orders.InventoryService"/> </sca:service> </sca:component> <sca:component name="OrderService"> <bean:implementation.bean class="org.switchyard.quickstarts.demos.orders.OrderServiceBean"/> <sca:service name="OrderService"> <sca:interface.java interface="org.switchyard.quickstarts.demos.orders.OrderService"/> </sca:service> <sca:reference name="InventoryService"> <sca:interface.java interface="org.switchyard.quickstarts.demos.orders.InventoryService"/> </sca:reference> </sca:component> </sca:composite> <transforms xmlns="urn:switchyard-config:transform:1.0"> <transform.java class="org.switchyard.quickstarts.demos.orders.Transformers" from="{urn:switchyard-quickstart-demo:orders:1.0}submitOrder" to="java:org.switchyard.quickstarts.demos.orders.Order"/> <transform.java class="org.switchyard.quickstarts.demos.orders.Transformers" from="java:org.switchyard.quickstarts.demos.orders.OrderAck" to="{urn:switchyard-quickstart-demo:orders:1.0}submitOrderResponse"/> </transforms> </switchyard>
Luckily, SwitchYard can generate the bulk of the above description based off of annotations in your application code, so you won't have to muck with this XML directly. For the portions that can't be generated, Forge tooling fills the gap nicely. While you certainly can play with the XML directly, we try to do what we can to protect you from those pointy angle brackets.
The composition of a SwitchYard application is defined using the Service Component Architecture Assembly Model , an open specification undergoing standardization in OASIS . For the nitty gritty details of the assembly model, please consult the SCA specification. This section provides a brief overview of the important elements:
<composite>
: single, top-level application element which defines a set of services, the relationships and dependencies of those services, and the linkage (if any) to services available outside the application.
<component>
: contains the implementation of a service and the dependencies of that service
<service>
: defines the name and interface for a service. This element is used inside a component definition to declare a service and can also appear inside a composite definition to indicate that a service is visible to outside applications.
<interface.XXX>
: the contract for a service. The type of the interface replaces 'XXX' in the definition. Supported interface types at this point are "java" and "wsdl".
<binding.XXX>
: the binding of a service. The type of the binding replaces 'XXX' in the definition. Example bindings include "binding.soap" and "binding.camel".
<implementation.XXX>
: the implementation of a service. The type of the implementation replaces 'XXX' in the definition. Example implementations include 'implementation.bean" and "implementation.camel".
The
<transforms>
section of SwitchYard configuration is used to define transformers local to your application. Similar to interfaces, bindings, and implementations, each transformer definition includes a type stuff in it's definition (e.g. transform.java, transform.smooks). The
from
and
to
definition on each transformer corresponds to the message type used on a service and/or reference interface used within SwitchYard. See the Transformer section of the User Guide for more information on individual transformer types.
As mentioned earlier, SwitchYard is capable of generating configuration off of annotations in your application code so that you don't have to hand edit XML configuration. The generated configuration is packaged up with your application as part of the Maven build lifecycle and placed in
target/classes/META-INF/switchyard.xml
Your application project can also include a descriptor in
src/main/resources/META-INF/switchyard.xml
This version of the configuration can be edited by the user directly and is also used by Forge tooling to specify configuration in response to SwitchYard forge commands. During the build process, the user-editable switchyard.xml is merged with any generated configuration to produce the final switchyard.xml in the target/ directory.
Testing your applications is dead simple with the comprehensive unit test support provided in SwitchYard. There are three primary elements to test support in SwitchYard:
SwitchYardRunner
:
JUnit
test
Runner
class which takes care of bootstrapping an embedded SwitchYard runtime and deploying a SwitchYard application for the test instance. Its behavior is influenced heavily by the optional
SwitchYardTestCaseConfig
annotation. Its runtime state is represented by the
SwitchYardTestKit
.
SwitchYardTestKit
: represents the runtime state of the deployed SwitchYard application instance deployed by
SwitchYardRunner
. Also provides access to a set of test utility methods for the test (e.g. assertion methods). The
SwitchYardTestKit
is reflectively injected into the test instance, if a property of type
SwitchYardTestKit
is declared in the test.
SwitchYardTestCaseConfig
: optional annotation allows additional information to be specified for controlling the behavior of the
SwitchYardRunner
.
Adding test support to your SwitchYard application is simply a matter of adding a dependency to the switchyard-test module in your application's pom.xml.
<dependency> <groupId>org.switchyard</groupId> <artifactId>switchyard-test</artifactId> <version>[release-version]</version> <!-- e.g. "0.2.0" --> <scope>test</scope> </dependency>
To take advantage of the test support in SwitchYard, your unit test should be annotated with the
SwitchYardRunner
JUnit
test
Runner
class.
SwitchYardRunner
takes care of creating and starting an embedded runtime for each test method. After the embedded runtime is started, the project containing the test is packaged as a SwitchYard application and deployed to it. The runtime state of the deployed SwitchYard test application is represented by an instance of the
SwitchYardTestKit
class, which is injected into the test when a property of type
SwitchYardTestKit
is declared in the test.
@RunWith(SwitchYardRunner.class) public class MyServiceTest { private SwitchYardTestKit testKit; @Test public void testOperation() { MyTestServiceHandler service = new MyTestServiceHandler(); // register the service... testKit.registerInOutService("MyService", service); // invoke the service and capture the response... Message response = newInvoker("MyService") .sendInOut("<create>A1234</create>"); // test the response content by doing an XML comparison with a // file resource on the classpath... testKit.compareXMLToResource(response.getContent(String.class), "/myservice/expected-create-response.xml"); } private class MyTestServiceHandler implements ExchangeHandler { // implement methods.... } }
The
SwitchYardTestKit
provides a set of utility methods for performing all sorts of deployment configuration and test operations.
The optional
SwitchYardTestCaseConfig
annotation can be used control the behavior of the
SwitchYardRunner
:
config
: allows the specification of a SwitchYard XML configuration file (switchyard.xml) for the test. The
SwitchYardRunner
will attempt to load the specified configuration from the classpath. If if fails to locate the config on the classpath, it will then attempt to locate it on the file system (e.g. within the project structure).
mixins
: composition-based method for adding specific testing tools to your test case. Each
TestMixIn
provides customized testing tools for things like service implementations, gateway bindings, and transformers. When a
TestMixIn
is annotated on a Test Class, the
SwitchYardRunner
handles all the initialization and cleanup (lifecycle) of the
TestMixIn
instance(s). It's also possible to manually create and manage
TestMixIn
instance(s) within your test class if (for example) you are not using the
SwitchYardRunner
.
scanners : add classpath scanning as part of the test lifecycle. This adds the same Scanner behavior as is available with the SwitchYard maven build plugin, but allows the scanning to take place as part of the test lifecycle. You will often find that you need to add Scanners if you want your test to run inside your IDE. This is because running your test inside your IDE bypasses the whole maven build process, which means the build plugin does not perform any scanning for you.
@RunWith(SwitchYardRunner.class) @SwitchYardTestCaseConfig(config = "testconfigs/switchyard-01.xml", mixins = {CDIMixIn.class, BPMMixIn.class}, scanners = {BeanSwitchYardScanner.class, TransformSwitchYardScanner.class}) public class MyServiceTest { @Test public void testOperation() { newInvoker("OrderService") .operation("createOrder") .sendInOnly("<order><product>AAA</product><quantity>2</quantity></order>"); } }
The
TestMixIn
feature allows you to selectively enable additional test functionality based on the capabilities of your application. The following MixIn types are available:
CDIMixIn
: boostraps a stand-alone CDI environment, automatically discovers CDI beans, registers bean services, and injects references to SwitchYard services.
HTTPMixIn
: client methods for testing HTTP-based services.
SmooksMixIn
: stand-alone testing of any Smoooks transformers in your application.
BPMMixIn
: utility methods for working with jBPM 5 Human Tasks (like starting/stopping a TaskServer).
Scanners add classpath scanning as part of the test lifecycle. This adds the same Scanner behavior as is available with the SwitchYard maven build plugin, but allows the scanning to take place as part of the test lifecycle. The following Scanners are available:
BeanSwitchYardScanner
: Scans for CDI Bean Service implementations.
TransformSwitchYardScanner
: Scans for Transformers.
BpmSwitchYardScanner
: Scans for @Process, @StartProcess, @SignalEvent and @AbortProcessInstance annotations.
RouteScanner
: Scans for Camel Routes.
RulesSwitchYardScanner
: Scans for @Rule annotations.
As shown above, injecting the
SwitchYardTestKit
instance into the test at runtime is simply a case of declaring a property of that type in the test class.
@RunWith(SwitchYardRunner.class) public class MyServiceTest { private SwitchYardTestKit testKit; // implement test methods... }
The SwitchYard test framework also injects other test support and metadata classes, which we outline in the following sections.
The
Deployment
instance can be injected by declaring a property of type
Deployment
.
@RunWith(SwitchYardRunner.class) public class MyServiceTest { private Deployment deployment; // implement test methods... }
The
SwitchYardModel
instance can be injected by declaring a property of type
SwitchYardModel
.
@RunWith(SwitchYardRunner.class) public class MyServiceTest { private SwitchYardModel model; // implement test methods... }
The
ServiceDomain
instance can be injected by declaring a property of type
ServiceDomain
.
@RunWith(SwitchYardRunner.class) public class MyServiceTest { private ServiceDomain serviceDomain; // implement test methods... }
The
TransformerRegistry
instance can be injected by declaring a property of type
TransformerRegistry
.
@RunWith(SwitchYardRunner.class) public class MyServiceTest { private TransformerRegistry transformRegistry; // implement test methods... }
TestMixIn
instance(s) can be injected by declaring properties of the specific type
TestMixIn
type.
@RunWith(SwitchYardRunner.class) @SwitchYardTestCaseConfig(mixins = {CDIMixIn.class, HTTPMixIn.class}) public class MyServiceTest { private CDIMixIn cdiMixIn; private HTTPMixIn httpIn; // implement test methods... }
Service
Invoker
instance(s) can be injected by declaring properties of type
Invoker
and annotating them with
@ServiceOperation
annotation.
@RunWith(SwitchYardRunner.class) @SwitchYardTestCaseConfig(config = "testconfigs/switchyard-01.xml") public class MyServiceTest { @ServiceOperation("OrderService.createOrder") private Invoker createOrderInvoker; @Test public void test_createOrder() { createOrderInvoker.sendInOnly("<order><product>AAA</product><quantity>2</quantity></order>"); } }
Note the annotation value is a dot-delimited Service Operation name of the form "service-name.operation-name".
The 0.2.0 release of SwitchYard supports the following deployment options:
SwitchYard AS6 (JBoss AS6 with SwitchYard pre-installed)
SwitchYard AS7 (JBoss AS7 with SwitchYard pre-installed)
Servlet Container (.war) deployment.
Deployment on SwitchYard AS6 (based on JBoss AS6) is the same old, same old. Simply copy your SwitchYard application to the the server's deploy directory and the SwitchYard deployer will take care of the rest.
cp myapp.jar $JBOSS_HOME/server/default/deploy/.
Undeploying a SwitchYard application is equally unsurprising. Just remove the app jar from the deploy directory like so:
rm $JBOSS_HOME/server/default/deploy/myapp.jar
Things are a bit different in SwitchYard AS7 (based on JBoss AS7). Auto-deploy still exists, but there are some subtle changes to the process. Deploy is still just a matter of copying the archive to the server, but the target directory should be
standalone/deployments
.
cp myapp.jar $JBOSS_HOME/standalone/deployments/.
To undeploy the application, you need to remove the
.deployed
marker file that is generated upon successful deployment of your application:
rm $JBOSS_HOME/standalone/deployments/myapp.jar.deployed
You can find more information on the ins and outs of deployment on JBoss AS7 in the
README.txt
file contained in the standalone/deployments directory of the distribution.
The following guidelines/tips apply when deploying Web Archive (.war) based SwitchYard deployments on a SwitchYard AS7 distribution.
See the "orders" quickstart (in the demos folder) as an example of .war deployment on SwitchYard AS7.
When running on SwitchYard AS7, remember that all the SwitchYard dependencies are provided by the container, so there's no need to bundle them inside your applications .war file. If using maven to build your application, simply set <scope>provided</scope> on all the SwitchYard dependencies.
Some maven POM tweaks are required when using both the
switchyard-plugin
and
maven-war-plugin
plugins to build a SwitchYard deployable .war file. By default, the
switchyard-plugin
expects the base
switchyard.xml
file to be located at
src/main/resources/META-INF/
within your maven project structure. This results in the
maven-war-plugin
putting the generated
switchyard.xml
(base
switchyard.xml
+ scanned additions) into
WEB-INF/classes/META-INF/
in the generated .war artifact. This is fine when deploying a .war file on Tomcat (or other pure Servlet Container), but not when deploying on SwitchYard AS7, because the SwitchYard AS7 deployer is what's doing the SwitchYard deployment work. When deploying a .war on SwitchYard AS7, we need the generated
switchyard.xml
to be in
META-INF/
, where it can be located by the SwitchYard AS7 deployer.
In order to do this we need to move away from the defaults a little bit. We need to locate the base
switchyard.xml
somewhere other than in
src/main/resources/META-INF/
, and configure the
switchyard-plugin
and
maven-war-plugin
accordingly. Lets assume we put the base
switchyard.xml
in
src/main/webResources/META-INF/
:
The POM configuration changes (away from the default) to the
switchyard-plugin
and
maven-war-plugin
plugins would be as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> ... <build> <plugins> ... <plugin> <groupId>org.switchyard</groupId> <artifactId>switchyard-plugin</artifactId> <version>0.3.0-SNAPSHOT</version> <configuration> <scannerClassNames> <param>org.switchyard.component.bean.config.model.BeanSwitchYardScanner</param> <param>org.switchyard.transform.config.model.TransformSwitchYardScanner</param> ... </scannerClassNames> <!-- Specify specific scan directories to reflect the new location of the base switchyard.xml ... --> <scanDirectories> <param>target/classes</param> <param>src/main/webResources</param> </scanDirectories> <!-- Specify different output directory for the generated switchyard.xml because we don't want it ending up in WEB-INF/classes/META-INF. The maven-war-plugin is configured to pick up the generated switchyard.xml from this new location... --> <outputDirectory>target/switchyard_xml</outputDirectory> </configuration> <executions> <execution> <goals> <goal>configure</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <!-- Java EE 6 doesn't require web.xml, Maven needs to catch up! --> <failOnMissingWebXml>false</failOnMissingWebXml> <webResources> <resource> <!-- Pick up the generated switchyard.xml from location specified by the switchyard-plugin... --> <directory>target/switchyard_xml</directory> </resource> </webResources> </configuration> </plugin> ... </plugins> </build> </project>
See the "orders" quickstart (in the demos folder) as an example of .war deployment on SwitchYard AS7.
SwitchYard provides a Servlet <listener-class> in support for deploying SwitchYard applications in a Java EE Web Application Container such as Tomcat or Jetty. To use this component, you simply need to include the
switchyard-deploy-webapp
artifact as a dependency on your web application project.
<dependency> <groupId>org.switchyard</groupId> <artifactId>switchyard-deploy-webapp</artifactId> <version>XXX</version> <!-- SwitchYard release version e.g. 0.2.0 --> </dependency>
You then need to add the
WebApplicationDeployer
class as a Servlet <listener-class> in your application's web.xml file.
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <listener> <listener-class>org.switchyard.deployment.WebApplicationDeployer</listener-class> </listener> <!-- etc... --> </web-app>
The
WebApplicationDeployer
class looks for a
switchyard.xml
file at
WEB-INF/classes/META-INF/switchyard.xml
, so if you are using maven to build your Web Application, you need to put your
switchyard.xml
in the
resources/META-INF
folder (i.e. not in
webapp/META-INF
). Your basic project structure should look as follows.
Once you've made these configurations, the process of creating the Web Application is no different to developing any other Web Application. Note however that, since this is only a new feature in version 0.2.0, we have tested only a very limited set of use cases:
A Basic SwitchYard .war deployment:
With all dependencies bundled in the application.
Deployable on Tomcat (and, in theory, on Jetty).
CDI Bean Services.
SOAP binding on the CDI Bean Service.
No JSF web ui components.
See the "webapp-deploy" quickstart (in the demos).
A more detailed SwitchYard .war deployment:
Without any dependencies bundled in the application and structured to work on the SwitchYard AS7 distribution.
Deployable on the SwitchYard AS7 distribution (Not deployable on Tomcat or Jetty).
More detailed CDI Bean Service.
SOAP binding on the CDI Bean Service.
JSF web ui interface on the CDI Bean Service. For more information on building a JSF interface on top of your SwitchYard CDI Bean Services, see the Bean Services documentation .
See the "orders" quickstart (in the demos).
SwitchYard integrates with Seam Forge to provide a set of rapid application development tools for service-oriented applications. Please consult the Getting Started guide for information on how to install Forge and the SwitchYard extensions to Forge.
The first thing you'll want to do with Forge is create a new project. This can be done inside the Forge shell using the new-project command.
$ forge ____ _____ / ___| ___ __ _ _ __ ___ | ___|__ _ __ __ _ ___ \___ \ / _ \/ _` | '_ ` _ \ | |_ / _ \| '__/ _` |/ _ \ \\ ___) | __/ (_| | | | | | | | _| (_) | | | (_| | __/ // |____/ \___|\__,_|_| |_| |_| |_| \___/|_| \__, |\___| |___/ [no project] tmp $ new-project --named syApp --topLevelPackage org.switchyard.examples.forge Use [/private/tmp/syApp] as project directory? [Y/n] Wrote /private/tmp/syApp/src/main/resources/META-INF/forge.xml ***SUCCESS*** Created project [syApp] in new working directory [/private/tmp/syApp]
At this point, you have an empty application with a few Maven facets installed. What's a facet you ask? Read on ....
Facets add capabilities to an application and to the forge environment itself. This allows SwitchYard to add dependencies to your application's pom based on the functionality you will be using, instead of sticking every possible SwitchYard dependency in the application by default. Facets are also used to add commands specific to SwitchYard itself and components which you will be using in your application. The following facets are currently available:
switchyard - core set of commands and dependencies for the SwitchYard runtime
switchyard.bean - commands and dependencies for Bean component services
switchyard.bpm - commands and dependencies for BPM component services
switchyard.rules - commands and dependencies for Rules component services
switchyard.soap - commands and dependencies for SOAP gateway bindings
switchyard.camel - commands and dependencies for Camel services and gateway bindings
Installing a facet can be done directly in the shell using the
install-facet
command.
[syapp] syapp $ project install-facet switchyard.soap ***SUCCESS*** Installed [switchyard.soap] successfully.
The following SwitchYard commands are available in Forge (grouped by facet).
switchyard
switchyard show-confi
g : displays the current state of your application's configuration, including services, references, and bindings.
switchyard promote-service
: promotes an internal application-scoped service to be visible to other applications.
s
witchyard promote-reference
: promotes an internal application-scoped reference so that it can be mapped to services provided in other applications.
s
witchyard create-service-test
: create a new unit test for a service.
s
witchyard get-version
: returns the version of SwitchYard used by the application.
s
witchyard trace-messages
: enables message tracing at runtime - all messages will be logged.
switchyard.bean
bean-service create
: creates a new CDI Bean service, consisting of a service interface and implementation class.
switchyard.bpm
bpm-service create
: creates a new BPM service, consisting of a service interface and implementation class.
switchyard.rules
rules-service create
: creates a new Rules service, consisting of a service interface and implementation class.
switchyard.camel
camel-service create
: creates a new XML or Java DSL Camel route.
camel-binding bind-service
: binds a service using a Camel endpoint URI.
camel-binding bind-reference
: binds a reference using a Camel endpoint URI.
switchyard.soap
soap-binding bind-service
: binds a service to a SOAP endpoint.
s
oap-binding bind-reference
: binds a reference to a SOAP endpoint.
A web based administration console is provided for viewing important aspects of a SwitchYard application.
Currently, the console provides a read-only view of the applications and services deployed to the SwitchYard runtime. This functionality will be extended in the future to provide access for configuring and monitoring SwitchYard applications and services.
Currently, the web console is only available for SwitchYard installations on AS7 and is included by default in the 0.2.0 SwitchYard AS7 release bundle.
The console is exposed on the "/switchyard" path of the web server instance in which it is deployed ( http://localhost:8080/switchyard by default). If you are using the SwitchYard AS7 release bundle, a link to the SwitchYard console is provided on the application server's welcome page ( http://localhost:8080 by default).
The console consists of a navigation panel on the left side of the screen and a content panel on the right. The navigation panel is divided into three sections:
Applications - SwitchYard applications deployed on the system.
Services - Services exposed by SwitchYard applications.
System - System details.
Clicking on one of the items listed under any section will cause its details to be displayed in the content panel to the right of the navigator.
Clicking on an item under the Applications section in the navigator will open the application details in the content panel. This panel displays a Services and Transformers tab.
The Services tab displays information about the services deployed by the application. This information includes the services exported by the application and the components used to implement the services.
The Services table displays the services exported by the application. The table provides the following details:
Name - The name of the service.
Promoted Service - The name of the component service providing the implementation for the service.
Clicking on an item in the Name column will load the service details for the selected service. Clicking on an item in the Promoted Service column will highlight the component used to implement the service in the Component Services table.
The Component Services table displays the components of which the application is comprised. This table provides the following details:
Name - The name of the component.
Interface - The interface implemented by the component.
Implementation - Provides a link for viewing the implementation details of the component.
Clicking on an item in the Implementation column will open a dialog detailing the component's implementation.
The Implementation dialog provides the following information:
The technology used to implement the component (e.g. Camel).
A list of references required by the component.
The raw configuration for the implementation.
The Transformers tab provides information about the transformers deployed by the application and is comprised of a table providing the following details:
From - The from type supported by the transformer.
To - The to type supported by the transformer.
Type - The implementation technology used by the transformer (e.g. Java, XSLT, etc.).
Clicking on an item under the Services section in the navigator will open the service details in the content panel. This panel displays details about the selected service. This includes:
Application - The application providing the service.
Interface - The interface provided by the service.
Promoted Service - The component used to implement the service.
Gateway Details - Details about the gateways provided for accessing the service.
The Gateway Details table provides the following information for each of the gateways provided for the service:
Type - The type of gateway (e.g. SOAP, HornetQ, etc.).
Configuration - Provides a link for viewing the configuration details of the gateway.
Clicking on an item in the Configuration column of the Gateways table opens a dialog displaying the raw configuration for the gateway.