Hi All,
This is a topic that I tried out during last two days and finally I was able to find a proper mechanism to do it. During these days I'm under a training and I assigned some exercise to complete. So the one exercise is to study the Axis2 and write service and client. I wrote a service and generate the .aar by maven plugin axis2-aar-maven-plugin and deploy it in a WSO2 application server. It support to deploy the .aar file.
Add following dependencies to pom.xml
Add axis2-aar-maven-plugin under <plugins></plugins>
So the next step is to create a client to test the service. This also can done with axis2-wsdl2code-maven-plugin easily. This will generate call back handler and service stub.
Using service stub directly is not a good practice. Therefore I wrote a wrapper class to call the stub methods. This makes directly call the service method and get the result.
Then I wrote a test class to test the web service through client. But to do all these, the prerequisite is the service must be up and running. :) The problem is each and every time when I build the project service must be available otherwise test cases fails. :( I can skip the test case and build the project but it's not the proper way to do it.
So what I want is find out is the way to dynamically create the axis2 service. Axis2 has rich set of features. I found that it's possible to programmatically create and deploy the service by providing the service class.
Note that I have to do some changes in generated stub (OrderServiceStub) to execute test due to namespace issue with programmatically deploy service. You can write a client inside the test method and test the service method. :)
You don't need to use mockup framework for testing. You can easily test your web services as they real time deploy in the server. :)
/** * Order model class */ public class Order { private Integer orderNumber; private String customerName; private Date orderDate; public Integer getOrderNumber() { return orderNumber; } public void setOrderNumber(Integer orderNumber) { this.orderNumber = orderNumber; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } }
/** * OrderService used to handle order process */ public interface OrderService { /** * Add order to process * * @param order {@link org.wso2.axis.model.Order} object which contains order details * @return message to the user */ String addOrder(Order order); /** * Get order details by order number * * @param orderNumber order number to get order details * @return {@link org.wso2.axis.model.Order} object which contains order details */ Order getOrderByOrderNumber(Integer orderNumber); /** * Get all oder details * * @return List of {@link org.wso2.axis.model.Order} objects which contains order details */ List<Order> getAllOderDetails(); }
/** * Implementation of {@link org.wso2.axis.service.OrderService} */ public class OrderServiceImpl implements OrderService { private static List<Order> orderList = new ArrayList<Order>(); /** * Add order to process * * @param order {@link org.wso2.axis.model.Order} object which contains order details * @return message to the user */ @Override public String addOrder(Order order) { String message = null; if(OrderValidator.validateOrder(order)){ orderList.add(order); message = "Order added successfully. ["+order.getOrderNumber()+", "+order.getOrderDate()+", "+order.getCustomerName()+"]"; } else { message = "Order not added. Please check your data."; } return message; } /** * Get order details by order number * * @param orderNumber order number to get order details * @return {@link org.wso2.axis.model.Order} object which contains order details */ @Override public Order getOrderByOrderNumber(Integer orderNumber) { Order orderFound = null; if(orderNumber != null){ for (Order order : orderList) { if(order.getOrderNumber().equals(orderNumber)){ orderFound = order; } } } return orderFound; } /** * Get all oder details * * @return List of {@link org.wso2.axis.model.Order} objects which contains order details */ @Override public List<Order> getAllOderDetails() { return orderList; } }
/** * Validate order object */ public class OrderValidator { public static boolean validateOrder(Order order) { boolean validate = false; if (order != null) { if (order.getOrderNumber() != null && order.getCustomerName() != null && order.getOrderDate() != null) { validate = true; } } return validate; } }
<?xml version="1.0" encoding="UTF-8"?> <service name="OrderService" targetNamespace="http://service.axis.wso2.org"> <schema schemaNamespace="http://service.axis.wso2.org"/> <description>Order web service</description> <!--compulsory parameter--> <parameter name="ServiceClass" locked="false">org.wso2.axis.service.impl.OrderServiceImpl</parameter> <!--common message receivers--> <messageReceivers> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> </messageReceivers> <!--addOder operation--> <operation name="addOrder" namespace="http://service.axis.wso2.org"> <actionMapping>urn:addOrder</actionMapping> </operation> <!--getOrderByOrderNumber operation--> <operation name="getOrderByOrderNumber" namespace="http://service.axis.wso2.org"> <actionMapping>urn:getOrderByOrderNumber</actionMapping> </operation> <!--getAllOderDetails operation--> <operation name="getAllOderDetails" namespace="http://service.axis.wso2.org"> <actionMapping>urn:getAllOderDetails</actionMapping> </operation> </service>
Add following dependencies to pom.xml
<dependency> <groupId>training.exercise</groupId> <artifactId>exercise-utils</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.apache.axis2</groupId> <artifactId>axis2</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>org.apache.axis2</groupId> <artifactId>axis2-adb</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>org.apache.axis2</groupId> <artifactId>axis2-transport-http</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>org.apache.axis2</groupId> <artifactId>axis2-transport-local</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>org.apache.axis2</groupId> <artifactId>axis2-xmlbeans</artifactId> <version>1.6.1</version> </dependency>
Add axis2-aar-maven-plugin under <plugins></plugins>
<!--Axis Archive generate plugin--> <plugin> <groupId>org.apache.axis2</groupId> <artifactId>axis2-aar-maven-plugin</artifactId> <version>${axis2.version}</version> <configuration> <aarName>OrderService</aarName> <servicesXmlFile>${basedir}/src/main/resources/META-INF/services.xml</servicesXmlFile> <includeDependencies>false</includeDependencies> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>aar</goal> </goals> </execution> </executions> </plugin>
So the next step is to create a client to test the service. This also can done with axis2-wsdl2code-maven-plugin easily. This will generate call back handler and service stub.
<!--Axis client generate plugin--> <plugin> <groupId>org.apache.axis2</groupId> <artifactId>axis2-wsdl2code-maven-plugin</artifactId> <version>${axis2.version}</version> <executions> <execution> <goals> <goal>wsdl2code</goal> </goals> <configuration> <wsdlFile>${basedir}/src/main/resources/wsdl/OrderService.wsdl</wsdlFile> <databindingName>adb</databindingName> <packageName>org.wso2.axis.stub</packageName> <outputDirectory>${basedir}/src/main/java</outputDirectory> <flattenFiles>true</flattenFiles> </configuration> </execution> </executions> </plugin>
/** * OrderServiceCallbackHandler Callback class, Users can extend this class and * implement * their own receiveResult and receiveError methods. */ public abstract class OrderServiceCallbackHandler {
/* * OrderServiceStub java implementation */ public class OrderServiceStub extends org.apache.axis2.client.Stub {
Using service stub directly is not a good practice. Therefore I wrote a wrapper class to call the stub methods. This makes directly call the service method and get the result.
/** * Wrapper class to call the {@see org.wso2.axis.stub.OrderServiceStub} */ public class OrderServiceClient { private static final Logger logger = LoggerFactory.getLogger(OrderServiceClient.class); private String endPoint; public OrderServiceClient(String endPoint) { this.endPoint = endPoint; } /** * call the addOrder web service method * * @param order {@see Order} object which contains data * @return result message */ public String addOrder(Order order){ String result = null; try { //instantiate OrderServiceStub OrderServiceStub orderServiceStub = getOrderServiceStub(); //instantiate AddOrder OrderServiceStub.AddOrder addOrder = new OrderServiceStub.AddOrder(); //instantiate Order OrderServiceStub.Order order1 = new OrderServiceStub.Order(); //Marshalling data order1.setCustomerName(order.getCustomerName()); order1.setOrderDate(order.getOrderDate()); order1.setOrderNumber(order.getOrderNumber()); addOrder.setOrder(order1); //call addOrder OrderServiceStub.AddOrderResponse addOrderResponse = orderServiceStub.addOrder(addOrder); result = addOrderResponse.get_return(); } catch (AxisFault axisFault) { axisFault.printStackTrace(); logger.error("Exception occurred in Service End Point", axisFault); } catch (RemoteException e) { e.printStackTrace(); logger.error("Exception occurred in calling service", e); } return result; } /** * call the getOrderByOrderNumber web service method * * @param orderNumber order number of given order * @return {@see Order} object which contains data */ public Order getOrderByOrderNumber(Integer orderNumber){ Order order = new Order(); try { //instantiate OrderServiceStub OrderServiceStub orderServiceStub = getOrderServiceStub(); //instantiate GetOrderByOrderNumber OrderServiceStub.GetOrderByOrderNumber getOrderByOrderNumber = new OrderServiceStub.GetOrderByOrderNumber(); //set order number getOrderByOrderNumber.setOrderNumber(orderNumber); //call getOrderByOrderNumber OrderServiceStub.GetOrderByOrderNumberResponse orderByOrderNumber = orderServiceStub.getOrderByOrderNumber(getOrderByOrderNumber); OrderServiceStub.Order order1 = orderByOrderNumber.get_return(); //Unmarshalling order order.setOrderNumber(order1.getOrderNumber()); order.setOrderDate(order1.getOrderDate()); order.setCustomerName(order1.getCustomerName()); } catch (AxisFault axisFault) { logger.error("Exception occurred in Service End Point", axisFault); } catch (RemoteException e) { logger.error("Exception occurred in calling service", e); } return order; } /** * call the getAllOrderDetails web service method * * @return */ public List<Order> getAllOderDetails() { List<Order> orderList = new ArrayList<Order>(); try { //instantiate OrderServiceStub OrderServiceStub orderServiceStub = getOrderServiceStub(); //instantiate GetAllOrderDetails OrderServiceStub.GetAllOderDetails getAllOderDetails = new OrderServiceStub.GetAllOderDetails(); //call getAllOrderDetails OrderServiceStub.GetAllOderDetailsResponse allOderDetails = orderServiceStub.getAllOderDetails(getAllOderDetails); //unmarshalling order OrderServiceStub.Order[] orders = allOderDetails.get_return(); for (OrderServiceStub.Order order : orders) { Order order1 = new Order(); order1.setOrderNumber(order.getOrderNumber()); order1.setOrderDate(order.getOrderDate()); order1.setCustomerName(order.getCustomerName()); orderList.add(order1); } } catch (AxisFault axisFault) { axisFault.printStackTrace(); logger.error("Exception occurred in Service End Point", axisFault); } catch (RemoteException e) { e.printStackTrace(); logger.error("Exception occurred in calling service", e); } return orderList; } /** * OrderServiceStub factory method * * @return {@see OrderServiceStub} object * @throws AxisFault */ private OrderServiceStub getOrderServiceStub() throws AxisFault { return new OrderServiceStub(endPoint); } }
Then I wrote a test class to test the web service through client. But to do all these, the prerequisite is the service must be up and running. :) The problem is each and every time when I build the project service must be available otherwise test cases fails. :( I can skip the test case and build the project but it's not the proper way to do it.
/** * Test class for OrderServiceClient * */ public class OrderServiceClientTest { public static final String ORDER_SERVICE_END_POINT="http://as.wso2.org:9763/services/OrderService?wsdl"; @Test public void testAddOrder() { OrderServiceClient orderServiceClient = getOrderServiceClient(); Order order = createOrder(1, new Date(), "Indika Sampath"); String result = orderServiceClient.addOrder(order); Assert.assertNotNull(result); System.out.println(result); } /** * OrderServiceClient factory method * * @return */ private OrderServiceClient getOrderServiceClient() { return new OrderServiceClient(ORDER_SERVICE_END_POINT); } /** * Order factory method * * @param orderNumber * @param orderDate * @param customerName * @return */ private Order createOrder(Integer orderNumber, Date orderDate, String customerName) { Order order = new Order(); order.setOrderNumber(orderNumber); order.setOrderDate(orderDate); order.setCustomerName(customerName); return order; } }
So what I want is find out is the way to dynamically create the axis2 service. Axis2 has rich set of features. I found that it's possible to programmatically create and deploy the service by providing the service class.
/** * Test class for OrderServiceClient * */ public class OrderServiceClientTest implements ServiceObjectSupplier { private OrderService orderService = new OrderServiceImpl(); private static int port = RemoteUtil.findFreePort(); private AxisServer server = new AxisServer(); @BeforeClass(alwaysRun = true) public void setUp() throws Exception { setupAxisServer(OrderService.class.getName(), OrderServiceClientTest.class.getName()); } @AfterClass public void tearDown() throws Exception { server.stop(); } @Test public void testAddOrder() { OrderServiceClient orderServiceClient = getOrderServiceClient(); Order order = createOrder(1, new Date(), "Indika Sampath"); String result = orderServiceClient.addOrder(order); Assert.assertNotNull(result); System.out.println(result); } /** * OrderServiceClient factory method * * @return */ private OrderServiceClient getOrderServiceClient() { return new OrderServiceClient("http://localhost:" + port + "/axis2/services/OrderService"); } /** * Order factory method * * @param orderNumber * @param orderDate * @param customerName * @return */ private Order createOrder(Integer orderNumber, Date orderDate, String customerName) { Order order = new Order(); order.setOrderNumber(orderNumber); order.setOrderDate(orderDate); order.setCustomerName(customerName); return order; } /** * Setup and start Axis-Server. * * @param serviceClassName * @throws org.apache.axis2.AxisFault */ private void setupAxisServer(String serviceClassName, String serviceObjectSupplierClassName) throws AxisFault, IllegalAccessException, InstantiationException { // Setup the custom port Map<String, TransportInDescription> transports = server.getConfigurationContext() .getAxisConfiguration() .getTransportsIn(); for (Map.Entry<String, TransportInDescription> objectEntry : transports.entrySet()) { TransportInDescription desc = objectEntry.getValue(); Parameter param = desc.getParameter("port"); param.setValue(Integer.toString(port)); } server.getConfigurationContext().getAxisConfiguration() .addParameter(Constants.SERVICE_OBJECT_SUPPLIER, serviceObjectSupplierClassName); //deploy OrderService server.deployService(serviceClassName); } /** * Callback-Method to retrieve the Service * * @param theService * @return Object */ public Object getServiceObject(AxisService theService) { String service = theService.getName(); if (service.equals("OrderService")) { return orderService; } return null; } }
Note that I have to do some changes in generated stub (OrderServiceStub) to execute test due to namespace issue with programmatically deploy service. You can write a client inside the test method and test the service method. :)
@Test public void testGetOrderByOrderNumber() throws AxisFault { RPCServiceClient serviceClient = getRpcServiceClient(); QName operationName = new QName("http://service.axis.wso2.org", "addOrder"); Order order = createOrder(1, new Date(), "Indika Sampath"); Object[] operationArguments = new Object[] { order }; Class[] returnTypes = new Class[] { String.class }; Object[] response = serviceClient.invokeBlocking(operationName, operationArguments, returnTypes); String result = (String) response[0]; Assert.assertNotNull(result); System.out.println(result); /*OrderServiceClient orderServiceClient = getOrderServiceClient(); Order order = createOrder(1, new Date(), "Indika Sampath"); String result = orderServiceClient.addOrder(order); Assert.assertNotNull(result); System.out.println(result);*/ } /** * Axis2 Client stuff * * @return * @throws org.apache.axis2.AxisFault */ private RPCServiceClient getRpcServiceClient() throws AxisFault { RPCServiceClient serviceClient = new RPCServiceClient(); Options options = serviceClient.getOptions(); EndpointReference targetEPR = new EndpointReference("http://localhost:" + port + "/axis2/services/OrderService"); options.setTo(targetEPR); return serviceClient; }
You don't need to use mockup framework for testing. You can easily test your web services as they real time deploy in the server. :)