This is the first of the two articles I am going to write about creating and installing WSO2 Carbon features to an existing WSO2 product. Most of the content here is based on the excellent tutorial written by Amal Gunatilake on the same topic. You can find it here.
WSO2 Carbon is an award-winning, component-based, service oriented platform for the enterprise-grade WSO2 middleware products. It is 100% open source and delivered under Apache License 2.0 by WSO2 Inc 1. WSO2 provides comprehensive “data-to-screen” open source products based on carbon which include WSO2 ESB, WSO2 API Manager, WSO2 Complex event processor, WSO2 Application Server, etc 2. Learning to write a carbon feature will give you the ability to customize these products very easily to meet your specific requirements.
Carbon (omitting the “WSO2” prefix from here on) can be thought of as “Eclipse for Server”. Similar to Eclipse, Carbon is built as a collection of well defined OSGi bundles (or components). This makes carbon a modular middleware platform which supports easy module installation, uninstallation, update, etc. WSO2 has built a very rich enterprise middleware products stack by exploiting modular nature of the Carbon platform 2.
Initially, WSO2 developed their products separately by allocating different teams for different products. These teams worked on developing their respective products by adding new features. Some of the customers who were using multiple products from WSO2 began requesting features which they found useful in one product (say, WSO2 Application Server) but not found in others (WSO2 ESB, etc.).
WSO2 came up with Carbon to increase reuse of code between their products. They packed a set of reusable components among the products in Carbon. Currently, Carbon core provides middleware components for enterprise security, clustering, logging, statistics, caching, tracing, throttling among other facilities and a management UI framework 4. A WSO2 product is essentially a combination of a subset of the core Carbon components and product specific features developed using Carbon framework (See the diagram below). This has made products more flexible to deploy, easy to extend and manage 3.
WSO2 carbon uses well established products like Equinox P2 (for provisioning Carbon components/features), Apache Axis2 (to create web services/clients as Carbon components) and Tomcat server (to deploy webapps, JSP support) to provide a rich framework for middleware application development.
A Carbon component is a set of OSGi bundles that is developed within the carbon framework. A Carbon component can use services provided by the Carbon framework like registry service, user manager service, etc. There are three main parts in a Carbon component: Server component (back end), UI component (front end) and a Service stub which facilitates the communication between the front end and the back end.
A Carbon feature is an installable form of one or more logically related Carbon components. Features can be installed into any carbon based product using its feature manager. Once a carbon component is built, it is released as a feature to enable easy install using a feature manager. Feature manager in WSO2 products use Equinox P2 (Provisioning Platform). Equinox P2 can be used to provision any OSGi bundle (remember that Carbon is based on OSGi, hence all the Carbon features / components are essentially OSGi bundles).
Equinox P2 looks for features inside local/remote repositories. Hence, the feature manager in WSO2 products also looks for features inside repositories. A feature repository is a collection of features from which we can select the feature(s) we want to install in our product.
There are three main tasks we need to do if we want to create and install a custom carbon feature.
Component development
We need to develop the Carbon component including back end component, front end component and common bundles (if there is any). Note that neither back end nor front end components are compulsory. All of these components are developed as OSGi bundles.
Feature development
Next we need to generate the corresponding feature for the component. Back end feature and front end feature are generated separately and then a composite feature is developed by combining them.
Installing the feature
We can build the feature along with the product (this is how the WSO2 develops the products) or install the feature using the feature manager after generating a feature repository. We are going to follow the second approach in this tutorials.
Let’s create a carbon feature that will mimic an order processing system and add it to a WSO2 product using the product’s feature manager. The feature will allow the user to add new orders and display the current orders in the front end UI. We’ll create a single project called order-processor
. We need our project to do the following tasks in the given order.
Let’s create three sub-projects inside order-processor
to do each of the above tasks. Also, We are going use Maven to build the project. If you are not familiar with Maven, please go through the Maven getting started guide. We’ll place the main pom.xml
(required by Maven) file inside the main project. pom.xml
and all the three subdirectories are empty for the time being. Below is the current directory structure. Let’s denote the project root by .
(i.e., absolute path of order-processor
).
order-processor
├── order-processor-components
├── order-processor-features
├── order-processor-repository
└── pom.xml
4 directories, 1 files
Let’s add content to the ./pom.xml
. We need to tell Maven about the sub-projects and give a project name, version, etc.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- POM version, required -->
<modelVersion>4.0.0</modelVersion>
<!-- Naming the project, required-->
<!-- groupId is unique among an organization -->
<groupId>org.wso2.carbon</groupId>
<!-- artifactId is the name of the project -->
<artifactId>order-processor</artifactId>
<!-- version of the project -->
<version>1.0.0</version>
<!-- Inform maven about the sub-projects -->
<modules>
<module>order-processor-components</module>
<module>order-processor-features</module>
<module>order-processor-repository</module>
</modules>
<!-- We use packaging type `pom` since this project is basically a
container for sub-projects -->
<packaging>pom</packaging>
<!-- A human friendly name -->
<name>WSO2 Carbon feature - Order Processor</name>
</project>
Now let’s concentrate on the sub-project order-processor-components
. We are going to create our server component, UI and the service stub inside this project. Let’s create the project folders for those sub-projects and the pom.xml
(all empty for the time being) inside the order-processor-components
project. Below is the updated directory structure.
order-processor
├── order-processor-components
│ ├── org.wso2.carbon.orderprocessor.server
│ ├── org.wso2.carbon.orderprocessor.stub
│ ├── org.wso2.carbon.orderprocessor.ui
│ └── pom.xml
├── order-processor-features
├── order-processor-repository
└── pom.xml
7 directories, 2 files
Now let’s write the pom.xml
for order-process-components
sub-project. Since this sub-project is also a container for a set of sub-projects, we can reuse most of the structure from order-processor
’s pom file. Additionally, we will have to add details of the parent project in it. This is done to make sure that the sub-projects properly inherit from the parent project. Our pom.xml
for order-processor-components
should look similar to something like below.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<groupId>org.wso2.carbon</groupId>
<artifactId>order-processor</artifactId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-processor-components</artifactId>
<packaging>pom</packaging>
<modules>
<module>org.wso2.carbon.orderprocessor.server</module>
<module>org.wso2.carbon.orderprocessor.stub</module>
<module>org.wso2.carbon.orderprocessor.ui</module>
</modules>
</project>
Note that the above pom inherits groupId
and version
from the parent pom.
Now let’s add a pom file to the order-processor-features
sub-project as well. We will use a very simple pom file for the time being. We will be updating this file later.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>order-processor</artifactId>
<groupId>org.wso2.carbon</groupId>
<version>4.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-processor-features</artifactId>
<packaging>pom</packaging>
<!-- We will update this pom later -->
</project>
Add a pom file to the sub-project order-processor-repository
as well. This will also be updated later.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>order-processor</artifactId>
<groupId>org.wso2.carbon</groupId>
<version>4.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-processor-repository</artifactId>
<packaging>pom</packaging>
<!-- We will update the pom later -->
</project>
Now the directory structure should look something similar to the following.
order-processor
├── order-processor-components
│ ├── org.wso2.carbon.orderprocessor.server
│ ├── org.wso2.carbon.orderprocessor.stub
│ ├── org.wso2.carbon.orderprocessor.ui
│ └── pom.xml
├── order-processor-features
│ └── pom.xml
├── order-processor-repository
│ └── pom.xml
└── pom.xml
7 directories, 4 files
Now we are done with the project structure. Time do some coding! Let’s start by creating the Carbon component. We need to update the three empty projects created under order-processor-components
.
Let’s write the server component for our order processor (which should be placed under org.wso2.carbon.orderprocessor.server
directory). This component should be able to store new orders and return existing orders. We will develop this as an Axis2 service (remember: carbon framework uses Axis2).
We are going to implement the server using two Java files: Order.java
and OrderProcessor.java
. Order.java
is a flimsy model of a real world order and OrderProcessor.java
is the service implementation which provides methods for creating new orders and getting the existing order list. Since we are building the project using Maven, we need to put these source files inside /src/main/java
respecting their package definitions. Below is an example implementation of the above two files.
package org.wso2.carbon.orderprocessor.server;
import org.wso2.carbon.orderprocessor.server.models.Order;
import java.util.HashMap;
public class OrderProcessor {
private int orderId;
private static HashMap<Integer, Order> orderMap = new HashMap<Integer, Order>();
public OrderProcessor() {
orderId = 1;
}
public int createOrder(int customerId, int itemId, int quantity) {
Order newOrder = new Order(customerId, itemId, orderId, quantity);
orderMap.put(new Integer(orderId), newOrder);
orderId++;
return orderId - 1;
}
public Order[] getOrders() {
return (Order[]) orderMap.keySet().toArray();
}
}
package org.wso2.carbon.orderprocessor.server.models;
import java.util.Arrays;
public class Order {
private int customerId;
private int itemId;
private int orderId;
private int quantity;
public Order(int customerId, int itemId, int orderId, int quantity) {
this.customerId = customerId;
this.itemId = itemId;
this.orderId = orderId;
this.quantity = quantity;
}
public int getCustomerId() {
return customerId;
}
public int getItemId() {
return itemId;
}
public int getQuantity() {
return quantity;
}
public int getOrderId() {
return orderId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
Also, we need to add an service description file, services.xml
for the created Axis2 service. services.xml
is used to convey information like the name of the service, class that contains the service implementation, transports used to connect with the server to Axis2 runtime. This file should be placed inside the META-INF
folder under the maven resources folder (/src/main/resources/
). You can read more about the services.xml
here.
<serviceGroup>
<service name="OrderProcessor" scope="transportsession">
<transports>
<transport>https</transport>
</transports>
<parameter name="ServiceClass">org.wso2.carbon.orderprocessor.server.OrderProcessor</parameter>
</service>
<parameter name="adminService" locked="true">true</parameter>
<parameter name="hiddenService" locked="true">true</parameter>
<parameter name="AuthorizationAction" locked="true">/permission/protected/manage</parameter>
</serviceGroup>
Now the only thing we are left with is writing the pom.xml
file for the server project. Since the back end server component needs to be packaged as an OSGi bundle, we need to build the project using the maven-bundle-plugin
. Read more about OSGi bundles and maven-bundle-plugin
in Creating a simple OSGi bundle and running it with Equinox OSGi container.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- Inherit from the parent project -->
<parent>
<groupId>org.wso2.carbon</groupId>
<artifactId>order-processor-components</artifactId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.orderprocessor.server</artifactId>
<!-- Backend server component is packaged as an OSGi bundle -->
<packaging>bundle</packaging>
<build>
<plugins>
<!-- Plugin used for creating an OSGi bundel -->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${pom.artifactId}</Bundle-Name>
<Export-Package>org.wso2.carbon.orderprocessor.server.*</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
After all these additions, the current directory structure should look something similar to the following.
order-processor
├── order-processor-components
│ ├── org.wso2.carbon.orderprocessor.server
│ │ ├── pom.xml
│ │ └── src
│ │ └── main
│ │ ├── java
│ │ │ └── org
│ │ │ └── wso2
│ │ │ └── carbon
│ │ │ └── orderprocessor
│ │ │ └── server
│ │ │ ├── models
│ │ │ │ └── Order.java
│ │ │ └── OrderProcessor.java
│ │ └── resources
│ │ └── META-INF
│ │ └── services.xml
│ ├── org.wso2.carbon.orderprocessor.stub
│ ├── org.wso2.carbon.orderprocessor.ui
│ └── pom.xml
├── order-processor-features
│ └── pom.xml
├── order-processor-repository
│ └── pom.xml
└── pom.xml
18 directories, 8 files
Now you can build the server project by typing mvn clean install
at the project root directory. You’ll see a new jar file named org.wso2.carbon.orderprocessor.server-1.0.0.jar
inside the newly created target
folder project root.
To create the service stub, we need the WSDL of the service we created. To get the WSDL, first copy the generated org.wso2.carbon.orderprocessor.server-1.0.0.jar
into <PRODUCT_HOME>/repository/components/dropins
in any WSO2 carbon based product. Here <PRODUCT_HOME>
denotes the root directory of the product. You can find a list of carbon based products here. After downloading, start the product by running <PRODUCT_HOME>/bin/wso2server.sh
if you are in a linux environment or <PRODUCT_HOME>/bin/wso2server.bat
if you are using Windows.
After the product has started, you’ll see a link to the product’s management console at the bottom of the command line. Open that link in the browser and login using a valid user-name and a password (You can use the default admin
account with the password admin
). In Manage --> Services --> List
, you’ll be able to see available services. Click the WSDL1.1
table entry of one of these services and you can see the WSDL file. Let’s say you click the WSDL entry for HelloService
. In the browser address bar, you will see an address like the following.
http://10.100.7.79:9765/services/HelloService?wsdl
Make sure that you have set HideAdminServiceWSDLs
to false in <PRODUCT_HOME>/repository/conf/carbon.xml
. Then replace HelloService
by OrderProcessor
and save the new WSDL file as OrderProcessor.wsdl
in some temporary location. Otherwise the WSDL will not be accessible.
Now we are going to build the project structure inside the stub project folder (org.wso2.carbon.orderprocessor.stub
). We don’t have any java code in this project. We only need to store OrderProcessor.wsdl
inside src/main/resources
and write the pom.xml
of the project to create the service stub from this WSDL file.
To create the service stub component, first we need to generate Java code from our WSDL file. Apache Axis2 provides a class (WSDL2Java) for this code generation. We can run this Java class as an Ant (another build tool used for building Java projects. Read more about Apache Ant) task inside our pom.xml
using maven-ant-run
plugin. Then we need to create an OSGi bundle out of the generated code. That can be done using maven-bundle-plugin
but this is a bit tricky.
By default, Maven does not recognize generated code as part of the project’s source. To bundle the generated code, we need Maven to see them as a part of the project’s source (then only the bundle plugin can ask for the generated code from Maven). So, to add the generated code directory to the Maven project, we are going to use build-helper-maven-plugin
. Our build process consists of running the above three Maven plugins in the correct order. Below is the pom.xml
which does this. Comments provide additional clarifications.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>order-processor-components</artifactId>
<groupId>org.wso2.carbon</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.orderprocessor.stub</artifactId>
<!-- We want to create an OSGi component -->
<packaging>bundle</packaging>
<!-- Dependencies for the code generation -->
<dependencies>
<dependency>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2</artifactId>
<version>1.6.1.wso2v10</version>
</dependency>
<dependency>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2-client</artifactId>
<version>1.6.1.wso2v10</version>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom</artifactId>
<version>1.2.11.wso2v4</version>
</dependency>
<dependency>
<groupId>wsdl4j.wso2</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2.wso2v4</version>
</dependency>
</dependencies>
<!-- Repository to download the dependencies -->
<repositories>
<repository>
<id>wso2-nexus</id>
<name>WSO2 internal Repository</name>
<url>http://maven.wso2.org/nexus/content/groups/wso2-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</releases>
</repository>
</repositories>
<build>
<plugins>
<!-- Run the WSDL2Java as a Ant task -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>source-code-generation</id>
<phase>process-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<!-- Set the classpath appropriately for running the WSDL2Java class
so that the dependencies can be found -->
<path id="wsdl2java.classpath">
<pathelement location="${settings.localRepository}
/org/apache/ws/commons/axiom/wso2/axiom/1.2.11.wso2v4/axiom-1.2.11.wso2v4.jar"/>
<pathelement location="${settings.localRepository}
/org/apache/axis2/wso2/axis2-client/1.6.1.wso2v10/axis2-client-1.6.1.wso2v10.jar"/>
<pathelement location="${settings.localRepository}
/org/apache/axis2/wso2/axis2/1.6.1.wso2v10/axis2-1.6.1.wso2v10.jar"/>
</path>
<java classname="org.apache.axis2.wsdl.WSDL2Java" fork="true">
<classpath refid="wsdl2java.classpath" />
<!-- Set the commandline argument passed to WSDL2Java class.
Pass the path of the WSDL to be used, path for writing output code
and packaging information. Read more about the commandline options
for WSDL2Java at http://axis.apache.org/axis2/java/core/docs/reference.html -->
<arg line="-uri src/main/resources/OrderProcessor.wsdl
-u -uw -o target/generated-code -p org.wso2.carbon.orderprocessor.stub"/>
</java>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<!-- Add the generated code to the Maven project -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>target/generated-code/src</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Create the OSGi bundle -->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Private-Package>
</Private-Package>
<Export-Package>
org.wso2.carbon.orderprocessor.*
</Export-Package>
<Import-Package>
!org.wso2.carbon.orderprocessor.*
</Import-Package>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Now the directory structure should look like the following.
order-processor
├── order-processor-components
│ ├── org.wso2.carbon.orderprocessor.server
│ │ ├── pom.xml
│ │ └── src
│ │ └── main
│ │ ├── java
│ │ │ └── org
│ │ │ └── wso2
│ │ │ └── carbon
│ │ │ └── orderprocessor
│ │ │ └── server
│ │ │ ├── models
│ │ │ │ └── Order.java
│ │ │ └── OrderProcessor.java
│ │ └── resources
│ │ └── META-INF
│ │ └── services.xml
│ ├── org.wso2.carbon.orderprocessor.stub
│ │ ├── pom.xml
│ │ └── src
│ │ └── main
│ │ └── resources
│ │ └── OrderProcessor.wsdl
│ ├── org.wso2.carbon.orderprocessor.ui
│ └── pom.xml
├── order-processor-features
│ └── pom.xml
├── order-processor-repository
│ └── pom.xml
└── pom.xml
21 directories, 10 files
Now let’s create the final part of our Carbon component: UI component. We are going to create the UI component in the org.wso2.carbon.orderprocessor.ui
project. UI component is nothing but a normal Java client (which depends on the generated stub in the previous section) exposed through JSP (JavaServer Pages). We need to create an additional XML file named component.xml
which supplies instructions to Carbon on how to load the created JSP file.
Our client programme is initialized according to the configuration information passed from the the Carbon’s Axis2 runtime. It uses the generated stub to call the services we exposed in our Service (OrderProcessor.java
). You can find a sample implementation of the client below. This file should be placed under src/main/java
after creating the directory structure to match the package definition.
package org.wso2.carbon.orderprocessor.ui;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.wso2.carbon.orderprocessor.server.models.xsd.Order;
import org.wso2.carbon.orderprocessor.stub.OrderProcessorStub;
import java.rmi.RemoteException;
public class OrderProcessorClient {
private OrderProcessorStub stub;
/* Constructor takes Axis2 client configurations passed from the Carbon runtime */
public OrderProcessorClient(ConfigurationContext configCtx, String backendServerURL, String cookie) throws Exception {
String serviceURL = backendServerURL + "OrderProcessor";
stub = new OrderProcessorStub(configCtx, serviceURL);
ServiceClient client = stub._getServiceClient();
Options options = client.getOptions();
options.setManageSession(true);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.COOKIE_STRING, cookie);
}
public Order[] getOrders() throws Exception{
try {
return stub.getOrders();
} catch (RemoteException e) {
String msg = "Cannot get the list of orders." +
" Backend service may be unavailable.";
throw new Exception(msg, e);
}
}
public int createOrder(int customerId, int itemId, int quantity) throws Exception {
try {
return stub.createOrder(customerId, itemId, quantity);
} catch (RemoteException e) {
String msg = "Cannot create the order." +
" Backend service may be unavailable.";
throw new Exception(msg, e);
}
}
}
Next we should write the index.jsp
file. Main task of the index.jsp
is to create an instance of the client programme with the existing Carbon context information and show current orders in a simple table after fetching them from the OrderProcessor
service. index.jsp
should be placed in a path starting from src/main/resources/web/
. In our example, let’s put our file inside a directory called orderprocessor
inside the web
directory.
<%@ page import="org.apache.axis2.context.ConfigurationContext" %>
<%@ page import="org.wso2.carbon.CarbonConstants" %>
<%@ page import="org.wso2.carbon.ui.CarbonUIUtil" %>
<%@ page import="org.wso2.carbon.utils.ServerConstants" %>
<%@ page import="org.wso2.carbon.ui.CarbonUIMessage" %>
<%@ page import="org.wso2.carbon.orderprocessor.ui.OrderProcessorClient" %>
<%@ page import="org.wso2.carbon.orderprocessor.models.xsd.Order" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib uri="http://wso2.org/projects/carbon/taglibs/carbontags.jar" prefix="carbon" %>
<%
String serverURL = CarbonUIUtil.getServerURL(config.getServletContext(), session);
ConfigurationContext configContext =
(ConfigurationContext) config.getServletContext().getAttribute(CarbonConstants.CONFIGURATION_CONTEXT);
String cookie = (String) session.getAttribute(ServerConstants.ADMIN_SERVICE_COOKIE);
OrderProcessorClient client;
Order[] orders;
try {
client = new OrderProcessorClient(configContext, serverURL, cookie);
orders = client.getOrders();
} catch (Exception e) {
CarbonUIMessage.sendCarbonUIMessage(e.getMessage(), CarbonUIMessage.ERROR, request, e);
%>
<script type="text/javascript">
location.href = "../admin/error.jsp";
</script>
<%
return;
}
%>
<div id="middle">
<h2>Order Processor</h2>
<div id="workArea">
<table class="styledLeft" id="moduleTable">
<thead>
<tr>
<th width="25%">Order ID</th>
<th width="25%">Customer ID</th>
<th width="25%">Item ID</th>
<th width="25%">Quantity</th>
</tr>
</thead>
<tbody>
<%
for(Order order:orders){
%>
<tr>
<td><%=order.getOrderId()%></td>
<td><%=order.getCustomerId()%></td>
<td><%=order.getItemId()%></td>
<td><%=order.getQuantity()%></td>
</tr>
<%
}
%>
</tbody>
</table>
</div>
</div>
Exercise : Update the index.jsp
to support addition of new orders.
Next we have to write the component.xml
which is used by the Carbon framework (or the product based on the Carbon framework) to link our index.jsp
. We are going to link index.jsp
to the Manage
menu in the navigation panel. component.xml
contains a set of configuration information needed by Carbon (or the Carbon based product) to do this. This file should be placed under src/main/resources/META-INF/
.
<component xmlns="http://products.wso2.org/carbon">
<menus>
<menu>
<id>orderprocessor_menu</id>
<i18n-key>orderprocessor.menu</i18n-key>
<i18n-bundle>org.wso2.carbon.orderprocessor.ui.i18n.Resources</i18n-bundle>
<parent-menu>manage_menu</parent-menu>
<link>../orderprocessor/index.jsp</link>
<region>region1</region>
<order>50</order>
<style-class>manage</style-class>
<require-permission>/permission/protected/manage</require-permission>
</menu>
</menus>
</component>
Below note describes what each element in the component.xml
does. This note is extracted from an article by Amal Gunatilake 5.
<id>
- Identifier to be used for our menu item<i18n-key>
- Key in i18n bundle (described later) which contains the display name of the menu item.<i18n-bundle>
- Path of the i18n bundle (we will be adding this in the next few steps).<parent-menu>
- Parent menu for the new link.<link>
- The relative path to index.jsp (starting from src/main/resources/web
)<region>
- Region where the link to be placed.<order>
- Defines the link order.<style-class>
- CSS styles for the new link.i18n
stands for internationalization (i-18 letters-n) of software products. It is a process which enables a software to support multiple languages. Properties of a software that needs to be localized (Documentation, menu names, label text, etc.) are stored in a i18n
bundle rather than hard coding them in the software itself. That’s why we are storing the name of our menu item in a separate i18n
bundle. Then we can easily change the menu name to a different language if we wanted to do so in future. According to our component.xml
, our i18n
bundle is the Resources.properties
file found under org/wso2/carbon/orderprocessor/ui/i18n/
inside src/main/resources/
. Note the extension .properties
at the end. This file contain the i18n
key orderprocessor.menu
and it’s value.
orderprocessor.menu=Orders
Finally, we will write the pom.xml
for the UI component. pom given below is self explanatory for the most part. Important things to remember are that we are going to pack the UI component as an OSGi bundle and that the UI component depends on the server stub. Additionally, when configuring the OSGi bundle, we add a separate instruction to inform the Carbon provisioning platform (Built on Equinox P2, remember?) that this is a Carbon UI component.
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>order-processor-components</artifactId>
<groupId>org.wso2.carbon</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.orderprocessor.ui</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.orderprocessor.stub</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Export-Package>
org.wso2.carbon.orderprocessor.*
</Export-Package>
<Import-Package>
*;resolution:=optional
</Import-Package>
<Carbon-Component>UIBundle</Carbon-Component>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Here is the final directory structure so that you can verify all the files are placed correctly.
order-processor
├── order-processor-components
│ ├── org.wso2.carbon.orderprocessor.server
│ │ ├── pom.xml
│ │ └── src
│ │ └── main
│ │ ├── java
│ │ │ └── org
│ │ │ └── wso2
│ │ │ └── carbon
│ │ │ └── orderprocessor
│ │ │ └── server
│ │ │ ├── models
│ │ │ │ └── Order.java
│ │ │ └── OrderProcessor.java
│ │ └── resources
│ │ └── META-INF
│ │ └── services.xml
│ ├── org.wso2.carbon.orderprocessor.stub
│ │ ├── pom.xml
│ │ └── src
│ │ └── main
│ │ └── resources
│ │ └── OrderProcessor.wsdl
│ ├── org.wso2.carbon.orderprocessor.ui
│ │ ├── pom.xml
│ │ └── src
│ │ └── main
│ │ ├── java
│ │ │ └── org
│ │ │ └── wso2
│ │ │ └── carbon
│ │ │ └── orderprocessor
│ │ │ └── ui
│ │ │ └── OrderProcessorClient.java
│ │ └── resources
│ │ ├── META-INF
│ │ │ └── component.xml
│ │ ├── org
│ │ │ └── wso2
│ │ │ └── carbon
│ │ │ └── orderprocessor
│ │ │ └── ui
│ │ │ └── i18n
│ │ │ └── Resources.properties
│ │ └── web
│ │ └── orderprocessor
│ │ └── index.jsp
│ └── pom.xml
├── order-processor-features
│ └── pom.xml
├── order-processor-repository
│ └── pom.xml
└── pom.xml
38 directories, 15 files
Now you can build the submodule order-processor-components
by typing mvn clean install
at the project root. In the next article we will discuss how to create a Carbon feature and install it to an WSO2 product.