WSO2 Carbon is the award-winning, component-based, service oriented platform for the enterprise-grade WSO2 middleware products stack. It is 100% open source and delivered under Apache License 2.0. The WSO2 Carbon platform is lean, high-performant and consists of a collection of OSGi bundles. - WSO2Prerequisites
- Java
- Maven
- IDE (eclipse/IdeaJ or any other IDE that supports maven project)
- Internet connection
- Any WSO2 Carbon product (I'm using WSO2 Application server. You can download any product that you like from WSO2 products page)
Getting Started
Here I'm going to create a simple order manager carbon component. This will consist of 3 modules.
Create a new maven project and give appropriate group ID and artifact ID. I'm using following group id and artifact id. These are very basic names. (Not based on standards naming conventions) You can use anything that you like.
- Back-end service
- Service stub
- Front-end (UI)
Create a new maven project and give appropriate group ID and artifact ID. I'm using following group id and artifact id. These are very basic names. (Not based on standards naming conventions) You can use anything that you like.
<groupId>org.wso2.carbon.sample</groupId>
<artifactId>ordar-manager</artifactId>
After creating the maven project you can delete the src folder if you like, because this will not contain any source code.
After creating the main project we must add 3 modules to that project. Make sure you set the parent of each module as the main project that was created first. If you are using IdeaJ, select the main project and then select File -> New Module. It will correctly point to the parent project. You just have to give the artifact Id.
In my project my modules are as follows
Back end Service Module
<parent>
<artifactId>ordar-manager</artifactId>
<groupId>org.wso2.carbon.sample</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>backend</artifactId>
Service Stub
<parent>
<artifactId>ordar-manager</artifactId>
<groupId>org.wso2.carbon.sample</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>service-stub</artifactId>
Front end
<parent>
<artifactId>ordar-manager</artifactId>
<groupId>org.wso2.carbon.sample</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>frontend</artifactId>
After adding the 3 modules your pom file of the main project should be similar to
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.sample</groupId>
<artifactId>ordar-manager</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>Backend</module>
<module>ServiceStub</module>
<module>Frontend</module>
</modules>
</project>
1. Creating the Back-end Service
My service class is OrderManager. I've created that inside the org.wso2.sample.carbon.order.manager package. This will have 2 service methods. One to add Items and other one is to retrieve the existing Items.
Item class
package org.wso2.sample.carbon.order.manager.beans;
import java.io.Serializable;
public class Item implements Serializable{
private int no;
private String name;
private float price;
public Item(int itemNo, String itemName, float itemPrice) {
this.no = itemNo;
this.name = itemName;
this.price = itemPrice;
}
public Item(){}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() { return price; }
public void setPrice(float price) {
this.price = price;
}
}
Note
I have implement the Serializable interface, since I'm going to use Carbon registry to store the Items in the registry.
Other thing to remember is if you overload the default constructor, You must explicitly define the default constructor, since you must adhere to the JavaBeans standards, which are use in underlying Axis2.
OrderManager class
package org.wso2.sample.carbon.order.manager;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.RegistryType;
import org.wso2.carbon.registry.api.Registry;
import org.wso2.carbon.registry.api.RegistryException;
import org.wso2.carbon.registry.api.Resource;
import org.wso2.sample.carbon.order.manager.beans.Item;
import java.io.*;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
public class OrderManager {
private static int orderID = 1;
Registry registry = null;
private static final String ORDER_PATH = "order_location";
private static Logger logger = Logger.getLogger(OrderManager.class.getName());
public OrderManager() {
HashMap orders = new HashMap();
orders.put(orderID++, new Item(1, "LG G3", 99900.00f));
orders.put(orderID++, new Item(3, "Iphone 6", 102000.00f));
orders.put(orderID++, new Item(3, "Sony Z3", 80000.00f));
orders.put(orderID++, new Item(4, "Nexus 6", 112000.00f));
orders.put(orderID++, new Item(5, "HTC M8", 88000.00f));
registry = CarbonContext.getThreadLocalCarbonContext().getRegistry(RegistryType.valueOf(RegistryType.LOCAL_REPOSITORY.toString()));
try {
Resource orderRes = registry.newResource();
orderRes.setContent(serialize(orders));
registry.put(ORDER_PATH, orderRes);
} catch (RegistryException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage());
}
}
public Item[] getOrders() {
Resource orderRes = null;
try {
orderRes = registry.get(ORDER_PATH);
if (orderRes != null) {
HashMap tmp = (HashMap) deserialize((byte[]) orderRes.getContent());
return tmp.values().toArray(new Item[tmp.values().size()]);
}
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (RegistryException e) {
logger.log(Level.SEVERE, e.getMessage());
}
return new Item[]{};
}
public void addOrder(Item item) {
Resource orderRes = null;
try {
orderRes = registry.get(ORDER_PATH);
if (orderRes != null) {
HashMap tmp = (HashMap) deserialize((byte[]) orderRes.getContent());
tmp.put(orderID++, item);
orderRes.setContent(serialize(tmp));
registry.put("orders_location", orderRes);
}
} catch (RegistryException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage());
}
}
private static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(b);
o.writeObject(obj);
return b.toByteArray();
}
private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream b = new ByteArrayInputStream(bytes);
ObjectInputStream o = new ObjectInputStream(b);
return o.readObject();
}
}
Since we are using Carbon registry following dependencies should be added to the back-end module.
<dependencies>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.core</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.api</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
Remember to use byte arrays when you are storing the values in the Carbon registry.Now we should write the service configuration (services.xml) for our service.
Create a folder called META-INF inside the resource folder of the back-end module. Inside the META-INF folder create services.xml file with following content. Change the service and service class names according to your project.
services.xml
<serviceGroup>
<service name="OrderManager" scope="transportsession">
<transports>
<transport>https</transport>
</transports>
<parameter name="ServiceClass">org.wso2.sample.carbon.order.manager.OrderManager</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 we must package the back-end module as a OSGI bundle. We are using maven-bundle-plugin to do that. We should also mentioned what are the packages that we are exporting from this module.
After adding the dependency and the plugin, pom file of the back-end module will be similar to following pom.
<?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>ordar-manager</artifactId>
<groupId>org.wso2.carbon.sample</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>backend</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.core</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.api</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${pom.artifactId}</Bundle-Name>
<Export-Package>org.wso2.sample.carbon.order.manager.*</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Now go to the back-end module pom file location from the terminal and use mvn clean install to build the module. If the build get success you will get a jar file like backend-1.0-SNAPSHOT.jar inside the target directory.
Then copy the created jar file to repository/components/dropins directory in the WSO2 product that you are using. Since I'm using WSO2 AS full path is like /home/thusitha/WSO2/wso2as-5.2.1/repository/components/dropins
We can't see the WSDL file of the created service directly accessing the https://localhost:9443/services/OrderManager?wsdl . That is because we have added this as a admin service and by default admin services WSDLs are hidden. In order to view the WSDL file open the carbon.xml file in the repository/conf and set the value of HideAdminServiceWSDLs as false
<HideAdminServiceWSDLs>false</HideAdminServiceWSDLs>
Now start the WSO2 AS and put the above URL in the browser (last part should be the Service name that you provide in the services.xml). Save the WSDL file in the disk. I'm saving it inside the resource folder of the service-stub module.
(If you want to check whether your service works as you expected, you can use SOAP UI like a tool. It is easy to comment out AutorizationAction in the services.xml if you do so)
2. Creating the Service-stub
We are using wsdl2java utility to create the stub. Other than that axiom, axis2, maven-antrun-plugin, build-helper-maven-plugin and maven-bundle-plugin are used.
Use following repository in the pom file of service-stub module.
<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>
At the end pom file of the service-stub module should be look like
<?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>ordar-manager</artifactId>
<groupId>org.wso2.carbon.sample</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
<artifactId>service-stub</artifactId>
<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>
<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>
<build>
<plugins>
<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>
<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" />
<arg line="-uri src/main/resources/OrderManager.wsdl -u -uw
-o target/generated-code -p org.wso2.sample.carbon.order.manager.stub"/>
</java>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<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>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<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.sample.carbon.order.manager.*
</Export-Package>
<Import-Package>
!org.wso2.sample.carbon.order.manager.*
</Import-Package>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
In the above pom file you can see that I've used the previously saved WSDL file. Do the necessary modifications to the pom file according to the your project.Go to pom file location of the service-stub and do a maven clean build. If you check the target folder inside the service stub module you can see the generated stub there. Final step is to create the UI to access the created service.
3. Creating the Front-end (UI)
We need to add the service stub module as a dependency to the front-end module since we need to use the generated stub to access the service. To do that open the pom file of the front-end module and add the service-stub as follows
dependency>
<groupId>org.wso2.carbon.sample</groupId>
<artifactId>service-stub</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
Finally full pom file of the front-end module should be like
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ordar-manager</artifactId>
<groupId>org.wso2.carbon.sample</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>frontend</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.wso2.carbon.sample</groupId>
<artifactId>service-stub</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>2.5.3</version>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Export-Package>
org.wso2.sample.carbon.order.manager.*
</Export-Package>
<Import-Package>
*;resolution:=optional
</Import-Package>
<Carbon-Component>UIBundle</Carbon-Component>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Next I'm creating the Client inside the front-end module, which will use the generated stub to access the back-end service.
Client class
package org.wso2.sample.carbon.order.manager.ui;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.sample.carbon.order.manager.beans.xsd.Item;
import org.wso2.sample.carbon.order.manager.stub.OrderManagerStub;
import java.rmi.RemoteException;
public class Client {
private OrderManagerStub stub;
public Client(ConfigurationContext ctx, String backendServerUrl, String cookie) throws Exception{
String serviceUrl = backendServerUrl + "OrderManager";
stub = new OrderManagerStub(ctx, serviceUrl);
ServiceClient serviceClient = stub._getServiceClient();
Options options = serviceClient.getOptions();
options.setManageSession(true);
options.setProperty(HTTPConstants.COOKIE_STRING, cookie);
}
public Item[] getItems() throws RemoteException {
return stub.getOrders();
}
public void addItem(Item item) throws RemoteException {
stub.addOrder(item);
}
}
Then create a directory named web inside resource folder. Inside the web folder, create another directory and named it as order-manager.
Create a file names index.jsp inside that. This is the jsp page that consist of the UI part. I will have a table of existing Items and a form to add new Items.
index.jsp
<%@ 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.sample.carbon.order.manager.ui.Client" %>
<%@ page import="org.wso2.sample.carbon.order.manager.beans.xsd.Item" %>
<%@ 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);
Client client;
Item[] orders;
try {
client = new Client(configContext, serverURL, cookie);
orders = client.getItems();
} catch (Exception e) {
CarbonUIMessage.sendCarbonUIMessage(e.getMessage(), CarbonUIMessage.ERROR, request, e);
return;
}
if ("POST".equalsIgnoreCase(request.getMethod())) {
try{
int itemCode = Integer.parseInt(request.getParameter("itemCode"));
String itemName = request.getParameter("itemName");
float itemPrice = Float.parseFloat(request.getParameter("itemPrice"));
Item newItem = new Item();
newItem.setNo(itemCode);
newItem.setName(itemName);
newItem.setPrice(itemPrice);
client.addItem(newItem);
}catch(Exception e){
e.printStackTrace();
}
}
%>
<div id="middle">
<h2>Order Management</h2>
<div id="workArea">
<table class="styledLeft" id="moduleTable">
<thead>
<tr>
<th width="20%">Order ID</th>
<th width="40%">Item Name</th>
<th width="40%">Price</th>
</tr>
</thead>
<tbody>
<%
for(Item order:orders){
%>
<tr>
<td><%=order.getNo()%></td>
<td><%=order.getName()%></td>
<td><%=order.getPrice()%></td>
</tr>
<%
}
%>
</tbody>
</table>
</br>
</br>
</br>
<form action="" method="POST">
<table>
<tr>
<td>Item Code</td>
<td><input type="text" id="itemCode" name="itemCode"></td>
</tr>
<tr>
<td>Item Name</td>
<td><input type="text" id="itemName" name="itemName"></td>
</tr>
<tr>
<td>Item Price</td>
<td><input type="text" id="itemPrice" name="itemPrice"></td>
</tr>
<tr>
<input type="submit" value="Add">
</tr>
</table>
</form>
</div>
</div>
Here you can see I've used some style classes and IDs. Those are predefined classes and IDs in the Carbon. Don't forget to import the carbon tag library as well.(Since IdeaJ community edition is not supporting JSP, you may find it very difficult to format and write the code. If you feel so use eclipse and copy back the code to IdeaJ)
Now we must add the UI component as a menu item. In order to do that you must create the component.xml file.
First create a folder named META-INF inside the resource folder of the front-end module and create a file named component.xml inside the META-INF folder with following content.
component.xml
<component xmlns="http://products.wso2.org/carbon">
<menus>
<menu>
<id>orderservice_menu</id>
<i18n-key>orderservice.menu</i18n-key>
<i18n-bundle>org.wso2.sample.carbon.order.manager.ui.i18n.Resources</i18n-bundle>
<parent-menu>manage_menu</parent-menu>
<link>../order-manager/index.jsp</link>
<region>region1</region>
<order>50</order>
<style-class>manage</style-class>
<require-permission>/permission/protected/manage</require-permission>
</menu>
</menus>
</component>
Here i18n-bundlevalue depend on the package that the created Client resides. Create folder structure according to the package name inside the web folder.
e.g my Client class resides in org.wso2.sample.carbon.order.manager.ui package. Therefore I must create folder structure as
org/wso2/sample/carbon/order/manager/ui
Then we have to create the resource bundle. To do so create a file named Resource.properties file. inside the above created folder structure.
e.g.
org/wso2/sample/carbon/order/manager/ui/Resources.properties
File content is
orderservice.menu = Order Service
Noteorderservice.menu is the i18n-key that we define in component.xml file.
Go to the pom file location of the front-end module and do a maven clean install
Deploy the Component
Now copy the generated jar files inside the back-end module and front-end module (No need of service-stub jar) in to the earlier mentioned dropins folder and restart the WSO2 AS and log back to the server.
You will see a menu item called Order Manager on left hand side menu bar and when you go to that menu item you will see something like
Note
If you don't get the menu item and don't get any errors in the console probably you might have some issues in resource bundle. Check if the location is correct according to the structure.
You can download the sample code from
https://drive.google.com/file/d/0B-jZl7AY9hiibE01X0hFYVg0SkU/view?usp=sharing
If I need server part to output in JSON format? For using with javascript in admin panel of WSO2 Carbon.
ReplyDeleteThere are some dependencies that are not available now. org.wso2.carbon.registry.core and org.wso2.carbon.registry.api should be downloaded separately (4.4.4 version) and added to the backend.
ReplyDeleteHello I am so delighted I located your blog, I really located you by mistake, while I was watching on google for something else, Anyways I am here now and could just like to say thank for a tremendous post and a all round entertaining website. Please do keep up the great work. carbon
ReplyDelete