Web Services

The Web Services Trust Model (WS-Trust)

The relationship with [WS-Security]

[WS-Security] defines the basic mechanisms for providing the message level security. This specification uses the base mechanisms and defines additional primitives and extensions for security token exchange to enable the issuance and dissemination of credentials within different trust domains.

In order to secure a communication between two parties, the two parties must exchange security credentials. However, each party needs to determine if they can “trust” the asserted credentials of the other party. [WS-Trust] is there to fulfill that process.

The Goal of [WS-Trust]

The main goal of [WS-Trust] is to enable applications to construct trusted [SOAP] message exchanges. This trust is represented through the exchange and brokering of security tokens. This specification provides a protocol agnostic way to issue, renew, and validate these security tokens. In order to facilitate this process, [WS-Trust] introduces a standard run-time component called Security Token Service (STS).

STS (Security Token Service)

In a typical [WS-Security] scenario, the client and the server can exchange keys between them in order to complete a secure communication. However, in a much larger enterprise level application, If we are going to take that approach, the relevant service needs to remember all the client locations and needs to update one by one when there is a key change happens at the service level.

In this kind of a scenario a STS can be quite a help, With STS around, the web service consumer doesn’t need to know the art of creating security tokens. Instead, it sends a request to the STS containing the requirements of the service consumer and the service provider and later attaches the returned security token to the outgoing SOAP message to the service provider. All the service consumer needs to do is to grab the returned token from the STS and attach it to the SOAP message. This basically eases the burden from the service consumer and they can concentrate on the business logic rather than the security aspects revolve around them.

SAML (Security Assertion Markup Language)

Although WS-Trust specification is having a protocol agnostic security token feature, SAML has been the preferred option mainly because of it is inter-operable capability over other protocols. SAML token assertions can provide three kinds of information related to service consumers.

1. Authentication Assertion – limit the service access for some authentication credentials (Name, Id, etc) of the service consumer.

2. Authorization Assertion – limit the service access for some authorization capabilities of the service consumer.

3. Attribute Assertion – limit the service access for certain attributes of the service consumer.

How it works

Now we are in a position to explain the basic flow of the “Web Service Trust Model”. Lets assume that both STS and service consumers are in an agreement to “trust” each other. Therefore, the given STS provider is able to issue security tokens (SAML) to service consumers to access their services. Basically this trust process can be explained in three steps as follows.

1. RST (Request Security Token) – Service Consumer request a security token from STS.

2. RSTR – (RequestSecurityTokenResponse) – STS will send the response as a SAML token with relevant security assertions

3. Now the service consumer is in a position to access the service provider with the SAML token, which was provided by STS.

All these SAML assertions are governed by policies defined (with the help of WS-SecurityPolicy) at service consumer, STS and service provider levels.

The above process is explained in the following diagram as well.

The above pattern creates an indirect trust relationship between the service provider and the STS instead of between the service provider and service consumer. As long as the service consumer is in the possession of a security token issued by a trusted STS, the service consumer is able to access the services provider.

So, this web service trust model is the base for the most of the Single Sign On and other Federated Identity Management systems in the SOA world. My next articles will discuss more into each of the technologies in a very detailed manner. Until then Happy SOA!

References:

1. WS-Trust Specification – http://specs.xmlsoap.org/ws/2005/02/trust/WS-Trust.pdf

2. WS-Trust with Fresh Banana Service – http://blog.facilelogin.com/2010/05/ws-trust-with-fresh-banana-service.html

3. Advanced SOA Security – SAML and WS-Trust – http://www.youtube.com/watch?v=YZNVyUc-3fQ

VN:F [1.9.22_1171]
Rating: 5.5/10 (2 votes cast)
VN:F [1.9.22_1171]
Rating: +2 (from 2 votes)

Ensuring End-End Security via ESB Secure Proxy

In my previous article about ensuring end-end security via ESB, primarily used a “Pass Through Proxy”. In that scenario, the message was not decrypted at the ESB and only decrypted at the service end. That is the easiest way to implement the message level end-end security, mainly because you do not do any message mediations at the ESB level and it was just a pass through.

But, in real world scenarios, ESBs are mostly used for message mediation activities. In such situations, we need the plain message at the ESB level and a decryption at the ESB level is required.

Typically a message will go through the following steps in this scenario.

1. The message is encrypted at the client.

2. The encrypted message is decrypted at the ESB.

3. Apply any mediations to the messages (if required)

4. Encrypt the message again at the ESB.

5. Decrypt the message at the service.

In order to apply above changes, you are required to do following configuration changes to the ESB. (We use WSO2ESB 4.0.0 here)

1. Add a security policy at the ESB Proxy. (For the decryption of the original message from the client)

2. Add a security policy at the ESB End Point. (For the encryption of the mediated message at the ESB)

Therefore, you are required to create a “Secure Proxy” at the ESB level and apply the above two policies for the secure proxy created and the respective end point.

Creating the Secure Proxy in ESB

Please do the following steps at the ESB. (WSO2ESB 4.0.0 is used here) The two policies should be created in the Registry prior executing these options.

1. Create a “Secure Proxy” and add the security policy for client message decryption.

2. Then select the created proxy and click the “Edit” link of the proxy. Then enter the rest of the required fields (i.e. WSDL URL, etc) and continue. When you reach “Step 2″, then click the “Edit” link of the Define End Point -> Define Inline. That will prompt you to the “Address End Point” configuration page. There, select the “Show Advanced Options” and check the “WS-Security” option. That will prompt you to enter the client Security Policy related to the back end service.

An Example

My previous article related to “Ensuring End-End security via ESB Pass through Proxy” was based on “SignEncr” Policy. which is basically Signing and Encrypting the message between both ends. If we apply the same scenario here, The two policies defined at the ESB level can be named here as “SignEncrClient” and “SignEncrService”.

SignEncrClient – This refers to the Step 1 of ESB Configurations.(See previous section)

SignEnctService – This referes to the Step 2 of ESB Configurations.

Respectively, in each above policy, you are required to set private key and public key alases correctly in order to make this communication happen.

In SignEncrClient the rampart configurations can be shown below.

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
	<ramp:user>server</ramp:user>
	<ramp:encryptionUser>client</ramp:encryptionUser>
	<ramp:passwordCallbackClass>com.crishantha.esb.security.ESBCallbackHandler</ramp:passwordCallbackClass>
	<ramp:signatureCrypto>
		<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
  		      <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
		      <ramp:property name="org.apache.ws.security.crypto.merlin.file">repository/samples/resources/security/esb.jks</ramp:property>
		      <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
		</ramp:crypto>
	</ramp:signatureCrypto>
	<ramp:encryptionCypto>
		<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
			<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
			<ramp:property name="org.apache.ws.security.crypto.merlin.file">repository/samples/resources/security/esb.jks</ramp:property>
			<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
		</ramp:crypto>
	</ramp:encryptionCypto>
</ramp:RampartConfig>

In SignEncrService the rampart configuration can be shown as follows

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
	<ramp:user>client</ramp:user>
	<ramp:encryptionUser>server</ramp:encryptionUser>
	<ramp:passwordCallbackClass>com.crishantha.esb.security.ESBCallbackHandler</ramp:passwordCallbackClass>
	<ramp:signatureCrypto>
		<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
  		      <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
		      <ramp:property name="org.apache.ws.security.crypto.merlin.file">repository/samples/resources/security/esb.jks</ramp:property>
		      <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
		</ramp:crypto>
	</ramp:signatureCrypto>
	<ramp:encryptionCypto>
		<ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
			<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
			<ramp:property name="org.apache.ws.security.crypto.merlin.file">repository/samples/resources/security/esb.jks</ramp:property>
			<ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
		</ramp:crypto>
	</ramp:encryptionCypto>
</ramp:RampartConfig>

Please check <ramp:user> and <ramp:encryptionUser> parameters to see the difference. In this example the keystore is named as esb.jks for both the policies. You can replace this by the default keystore of the ESB or can maintain a separate keystore to maintain the communication with other parties.

The ESBCallbackHandler

As you already know, in order to validate token based authentications, WS-Security uses the CallBack Handler classes especially for token/ password validations. Since the decryption happens at the ESB level, it is required to specify the Call Back Handler class within the ESB. For that you are required to specify the modified ESBCallBackHandler class and store as a jar file in the <ESB_HOME>/components/libs folder.

A sample ESBCallbackHandler class which we used is given below.

package com.crishantha.esb.security;

import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;

public class ESBCallbackHandler
  implements CallbackHandler
{
  public void handle(Callback[] callbacks)
    throws IOException, UnsupportedCallbackException
  {
    WSPasswordCallback pc = (WSPasswordCallback)callbacks[0];
    int usage = pc.getUsage();

    if (usage == 3) {
      pc.setPassword("password");
    }
    else if (usage == 1) {
      pc.setPassword("password");
    }
    else
      pc.setPassword("password");
  }
}

Actually the above created proxy service is stored in the <ESB_HOME>/repository/deployment/server/synapse-configs/default/proxy-services/ folder. You can re-check the configurations by moving to this folder. The following proxy configuration is the one for above configurations.

<proxy xmlns="http://ws.apache.org/ns/synapse" name="TestSecureProxy" transports="https,http" statistics="disable" trace="disable" startOnLoad="true">
   <target>
      <inSequence>
         <header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" name="wsse:Security" action="remove" />
      </inSequence>
      <outSequence>
         <send />
      </outSequence>
      <endpoint>
         <address uri="http://localhost:8080/axis2/services/TemperatureConversionService">
            <enableSec policy="gov:/SignEncrService" />
         </address>
      </endpoint>
   </target>
   <publishWSDL uri="http://localhost:8080/axis2/services/TemperatureConversionService?wsdl" />
   <enableSec />
   <policy key="gov:/SignEncrClient" />
   <policy key="conf:/repository/axis2/service-groups/TestSecureProxy/services/TestSecureProxy/policies/urn:uuid:BAAA739BE65334FCD91420475175687" />
</proxy>
VN:F [1.9.22_1171]
Rating: 10.0/10 (2 votes cast)
VN:F [1.9.22_1171]
Rating: +2 (from 2 votes)

Invoking a HTTPS web service end point via ESB

In one of my previous articles, I have explained how we can invoke a web service via HTTPS. In that scenario,the client and the service were authenticated either 1-way/2-way SSL.

In the typical SOA environment, generally the client does invoke another Proxy Service within the ESB and that Proxy Service will invoke the HTTPS enabled remote web service. Therefore, the HTTPS communication happens usually between the ESB and the remote web service.

To explain this scenario in detail, I am using the WSO2ESB (version 4.0.0) and a web service enabled by Axis2. The same web service, which was used in my previous article is used again here for brevity.

Steps to be followed:

1. Import the public key of the remote web service to the “Client Truststore” of the ESB.

keytool -import -file service.cer -alias service -keystore client-truststore.jks

The client-truststore.jks of the WSO2ESB can be found at the /wso2esb-4.0.0/repository/resources/security folder.

2. Change the ESB configurations in order to facilitate the mutual SSL authentication.

In order to get rid of the certificate host verification errors you are required to do the following change to the axis2.xml. (/wso2esb-4.0.0/repository/conf)

<parameter name="HostnameVerifier">AllowAll</parameter>

3. Change the Proxy Service settings in order to invoke the HTTPS enabled web service end point. The WSO2ESB Proxy setting is given below. Here a Custom Proxy (in WSO2ESB 4.0.0) is created with a HTTPS end point. The given web service was discussed in my previous article. [1]

<proxy name="TempHttpsProxy" transports="https http" startOnLoad="true" trace="enable">
    <target>
         <endpoint>
             <address uri="https://localhost:8443/axis2/services/TemperatureConversionService"/>
         </endpoint>
         <outSequence>
             <send/>
         </outSequence>
     </target>
     <publishWSDL uri="http://localhost:8080/axis2/services/TemperatureConversionService?wsdl"/>
</proxy>

Thats it!. If you have done above steps neatly, you are good to go with this setup. Happy SOA!

References:

1. Apache Rampart (Part 2) – Transport Level Security – http://crishantha.com/wp/?p=628

VN:F [1.9.22_1171]
Rating: 10.0/10 (3 votes cast)
VN:F [1.9.22_1171]
Rating: +3 (from 3 votes)

Ensuring end-end security via ESB Pass through Proxy

In one of my previous posts, I was discussing about the end-end security and how Apache Rampart can be leveraged in each security policy scenario. When we have end-end security the security is ensured at the message level from one end to the other end. The message will not be generally exposed to any other party especially at each hop in between.

In typical SOA environment, the messages are mediated through Enterprise Service Buses (ESB). In such scenario, the ESB usually should be configured to cater to the above scenario, which is to ensure the message integrity and its confidentiality throughout its entire journey from the start to the end.

This article will discuss this simple but very important aspect and I am sure will be beneficial to anybody who are involved in the Enterprise Integration.

The Development Environment

The Web Service Security Implementation: Apache Rampart 1.5

The ESB: WSO2 ESB 4.0.0

The Source Code

You may download the source code, which was given under any of my previous articles related to Apache Rampart.

Article 1: Apache Rampart (Part 3) – Message Level Security – Asymmetric Binding (SignOnly)

Article 2: Apache Rampart (Part 4) – Message Level Security – Asymmetric Binding (SignAndEncrypt)

The ESB Configurations

Here, you are required to create a simple “Pass Through Proxy” without applying any security at the proxy level. Once you completed the normal pass through configurations, you need to do the following simple configuration change in order to retain the SOAP message integrity from one end to the other end.

Step 1: Create a “Pass Through Proxy” – Here it is called “SignEncrProxy”.

You need to enter the End Point WSDL as http://localhost:8080/axis2/services/TemperatureConversionService?wsdl and set the rest of the configurations as default.

Once you create the “SignEncrProxy”, just click the proxy at the list of proxy services main page. Then you will get the following screen. (See Diagram 1) In this, just as it indicated by a red circle, click the “Edit” link to change the default configurations.

Diagram 1

Step 2: Then move to the Step 2 by clicking the “Next” button. You will see a page like follows (See Diagram 2) Here, as indicated by the red circle, click the “Edit” link.

Diagram 2

Step 3: In this step, select the “Add Property” link under the “Endpoint properties”. Then add the “preserveProcessedHeaders” to the “Name” field and enter “true” at the “Value” field. The rest leave as it is. Finally you may save the proxy.

Diagram 3

If you do not apply this configuration change, you may get an exception similar to the following:

Exception in thread "main" org.apache.axis2.AxisFault: Missing wsse:Security header in request
	at org.apache.axis2.util.Utils.getInboundFaultFromMessageContext(Utils.java:435)
	at org.apache.axis2.description.OutInAxisOperationClient.handleResponse(OutInAxisOperation.java:371)
	at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:417)
	at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:229)
	at org.apache.axis2.client.OperationClient.execute(OperationClient.java:165)

Happy coding!

VN:F [1.9.22_1171]
Rating: 10.0/10 (1 vote cast)
VN:F [1.9.22_1171]
Rating: +2 (from 2 votes)

Apache Rampart (Part 5) – Transport Level Security + UserNameToken

An Introduction

In the Part 02 of the Apache Rampart series, we discussed about how we can secure the transport level channel using HTTPS. But, in that scenario the client was not authenticated. The client authentication can be done having a supporting token generated from the client side. The UserNameToken is one of those simple supporting tokens to utilize this aspect.

The Development Environment

Language: Java

IDE: Eclipse

Build Tool: Apache Ant

The Scenario

Rampart configuration: Policy based configuration with Transport Level Security

Authentication: UserNameToken as the supporting token for the authentication

Creating Keystores for the service and the client

In order to make sure a 1-way SSL communication between the client and the server, you are required to follow the steps given below.

Step 1: Creating a server keystore (server.jks)

keytool -genkey -alias server -keyalg RSA -keysize 1024 -keypass password -keystore server.jks -storepass password

Step 2: Extract (export) server public key/ certificate from the server keystore (server.jks)

keytool -alias server -export -keystore server.jks -storepass password -file server.cer

Step 3: Import the extracted public key to client keystore (clienttrust.jks)

keytool -import -alias server -file server.cer -keystore clienttrust.jks -storepass password

The above four steps generated a key pair at the server level and its public certificate is added to the client side truststore to complete the 1-way SSL communication.

Creating the Service

A temperature conversion example being used here.

Step 1: Setting up the Eclipse environment to create a code-first/bottom-up web service. You can refer my previous article on “Writing a Code-First web services using Axis2“) for a detail explanation on this.

Step 2: Writing the service. Here the service name is TemperatureConversionService.

package crishantha.rampart;

public class TemperatureConversionService {
	public float celcius2farenhit(float celcius) {

		return (celcius*(9/5)) + 32;
	}

	public float farenhit2celcius(float farenhit) {

		return ((farenhit-32)*5)/9;
	}

 }

Step 3: Writing the Password Call Back Handler

The UsernameToken authentication at the server level happens with the help of the Callback Handler.

package crishantha.rampart;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.ws.security.WSPasswordCallback;

public class PWCBHandler implements CallbackHandler{

	public void handle(Callback[] callbacks) throws IOException,
			UnsupportedCallbackException {

		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pwcb = (WSPasswordCallback) callbacks[i];
			String id = pwcb.getIdentifer();

			if(pwcb.getIdentifer().equals("apache") && pwcb.getPassword().equals("password")) {
                // If authentication successful, just return
                return;
            } else {
                throw new UnsupportedCallbackException(callbacks[i], "Authentication failure");
            }
		}
	}
}

Step 4: Creating the Web Service Archive (aar) file.

Right click the TemperatureConversionService.java class and use New –> Other –> Web Service and follow the normal steps to create the web service. Now check the WebContent/WEB-INF/services folder and you will find out that there is a new folder created under the name “TemperatureConversionService”. Go to the META-INF folder and replace the current services.xml file with the following Policy written services.xml.

<service name="TemperatureConversionService" >

	<module ref="rampart"/>

	<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>
	<parameter name="ServiceClass" locked="false">crishantha.rampart.TemperatureConversionService</parameter>

    <wsp:Policy wsu:Id="UsernameTokenOverHTTPS" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
		<wsp:ExactlyOne>
		  <wsp:All>
			<sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
			  <wsp:Policy>
				<sp:TransportToken>
				  <wsp:Policy>
					<sp:HttpsToken RequireClientCertificate="false"/>
				  </wsp:Policy>
				</sp:TransportToken>
				<sp:AlgorithmSuite>
				  <wsp:Policy>
					<sp:Basic256/>
				  </wsp:Policy>
				</sp:AlgorithmSuite>
				<sp:Layout>
				  <wsp:Policy>
					<sp:Lax/>
				  </wsp:Policy>
				</sp:Layout>
				<sp:IncludeTimestamp wsp:Optional="true"/>
			  </wsp:Policy>
			</sp:TransportBinding>
			<sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
				<wsp:Policy>
					<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient" />
			  </wsp:Policy>
			</sp:SignedSupportingTokens>
			<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
				<ramp:passwordCallbackClass>crishantha.rampart.PWCBHandler</ramp:passwordCallbackClass>
			</ramp:RampartConfig>
		  </wsp:All>
		</wsp:ExactlyOne>
	</wsp:Policy>
</service>

Now go to the command line and use the jar -cvf TemperatureConversionService.aar * to create the service archive file. This .aar file can be placed in the TOMCAT_HOME/webapps/axis2/WEB-INF/services folder. If the created web service was deployed properly,

You can check it with the following link.

http://localhost:8080/axis2/services/TemperatureConversionService?wsdl

If you can see the WSDL with the added policy, you are on the right track.

However we need to do some more work at the server side. We need to make sure the running TOMCAT instance is capable enough to cater HTTPS transport. For that you need to do a slight change to the server.xml of the TOMCAT server. As shown below please uncomment the existing HTTPS related block and make sure the following is maintained.

    <Connector port="8443" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
               keystoreFile="/home/crishantha/testkeys/server.jks"
               keystorePass="password"
               />

Now you can further test the previous WSDL with https. See the link below.

https://localhost:8443/axis2/services/TemperatureConversionService?wsdl

This should show the same WSDL output as shown with http mode. Now, you are done with your web service creation and deployment.

Creating the Service Client

Step 1: Create a Dynamic Web Project and open up a command line and move to the created project and execute the following command to create relevant stubs for the created web service.

wsdl2java.sh -uri http://localhost:8080/axis2/services/TemperatureConversionService?wsdl -p crishantha.rampart.client -uw -o .

This will create relevant stub class files and related service call back classes. Now it is the time to create our own client. Check the test client code that I have created.

package crishantha.rampart.client;

import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;

public class TemperatureConversionClient {

	public static void main(String[] args) throws Exception {
            ConfigurationContext ctx =                ConfigurationContextFactory.createConfigurationContextFromFileSystem("/home/crishantha/WSRampart/axis2-1.5.1/repository", null);
            TemperatureConversionServiceStub stub = new TemperatureConversionServiceStub(ctx, "https://localhost:8443/axis2/services/TemperatureConversionService");

            ServiceClient sc = stub._getServiceClient();
            sc.engageModule("rampart");

            Options options = sc.getOptions();
            options.setUserName("apache");
            options.setPassword("password");

            System.setProperty("javax.net.ssl.trustStore", "/Path/To/clienttrust.jks");
            System.setProperty("javax.net.ssl.trustStorePassword", "password");

            float farenhitValue = stub.celcius2Farenhit(100);
            System.out.println("Farenhit value for 100 C is : " + farenhitValue);
     }
}

Step 2: Creating the client archive (JAR)

Then, you can use the eclipse created build.xml file to create a client JAR file. Just type “ant” by moving to the eclipse working project directory. After creating the JAR, execute the following script.

java -Daxis2.repo=$AXIS2_HOME/repository \
     -Djava.ext.dirs=$AXIS2_HOME/lib:`pwd`/build/lib:$JAVA_HOME/jre/lib/security:$JAVA_HOME/jre/lib/ext \
      crishantha.rampart.client.TemperatureConversionClient

If you have followed all the steps correctly, you should see the output on your console. This could be something like below:

Farenhit value for 100 C is : 132.0

The source code can be found here.

VN:F [1.9.22_1171]
Rating: 8.8/10 (4 votes cast)
VN:F [1.9.22_1171]
Rating: +1 (from 3 votes)

Apache Rampart (Part 4) – Message Level Security – Asymmetric Binding (SignAndEncrypt)

An Introduction

This is the Part 04 of the Apache Rampart sample series. The Part 01 gave an introduction to the Apache Rampart stack and related WS-Security standards. The Part 02 was all about dealing with Transport Level Security with Apache Rampart. The Part 03 talked about the Message Level Security using the Asymmetric Binding signing both the header and the body. This tutorial further discusses the Message Level Security Asymmetric Binding feature with both signing and encryption.

The Development Environment

Language: Java

IDE: Eclipse

Build Tool: Apache Ant

The Scenario

Rampart configuration: Policy based configuration with Message Level Security. Here the Policies are defined to sign the header-body and to encrypt the body of the message.

Creating Keystores for the service and the client

The signing makes sure authentication, integrity and non-repudiation. The encryption makes sure the confidentiality. For signing at the senders side the private key of the sender is used and at the receivers side the sender’s public key is used. The encryption happens at the senders side using the receiver’s public key and at the receiver’s side decryption happens using the receiver’s private key. Therefore, it is required to exchange public keys of both sender and the receiver. Follow the same steps that I have followed in Part 02 or Part 03 to carry out this task.

Creating the Service

Here we consider a simple scenario to demonstrate the Message Level Security. A temperature conversion example being used here.

Step 1: Setting up the Eclipse environment to create a code-first/bottom-up web service. You can refer my previous article on “Writing a Code-First web services using Axis2“) for a detail explanation on this.

Step 2: Writing the service. Here the service name is TemperatureConversionService.

package crishantha.rampart;

public class TemperatureConversionService {
	public float celcius2farenhit(float celcius) {

		return (celcius*(9/5)) + 32;
	}

	public float farenhit2celcius(float farenhit) {

		return ((farenhit-32)*5)/9;
	}

 }

Step 3: Writing the Password Call Back Handler

public class PWCBHandler implements CallbackHandler {

 public void handle(Callback[] callbacks) throws IOException,
              UnsupportedCallbackException {

 for (int i = 0; i < callbacks.length; i++) {

    WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
    String id = pwcb.getIdentifer();

    if ("client".equals(id)) {
        pwcb.setPassword("password");
    } else if ("server".equals(id)) {
        pwcb.setPassword("password");
    }
 }
 }
}

P.Note: The above Callback class is used to extract the private key of the server and the public key of the sender. Both of these are extracted from the server keystore. (The keystore details are mentioned under the server policy). Here, the “server” is the alias of the public-private key pair and the “client” is the alias of the sender (client) public key. In this example, both have the same password, which is “password”.

Step 4: Creating the Web Service Archive (aar) file. Right click the TemperatureConversionService.java class and use New –> Other –> Web Service and follow the normal steps to create the web service. Now check the WebContent/WEB-INF/services folder and you will find out that there is a new folder created under the name “TemperatureConversionService”. Go to the META-INF folder and replace the current services.xml file with the following Policy written services.xml.

<service name="TemperatureConversionService">
    <module ref="rampart"/>
	<parameter name="ServiceClass" locked="false">crishantha.rampart.TemperatureConversionService
	</parameter>
	<operation name="celcius2farenhit">
        <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
    </operation>
	<operation name="farenhit2celcius">
        <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
    </operation>   

   <wsp:Policy wsu:Id="SigEncr"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:AsymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                        <sp:InitiatorToken>
                            <wsp:Policy>
                                <sp:X509Token
                                        sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                                    <wsp:Policy>
                                        <sp:WssX509V3Token10/>
                                    </wsp:Policy>
                                </sp:X509Token>
                            </wsp:Policy>
                        </sp:InitiatorToken>
                        <sp:RecipientToken>
                            <wsp:Policy>
                                <sp:X509Token
                                        sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                                    <wsp:Policy>
                                        <sp:WssX509V3Token10/>
                                    </wsp:Policy>
                                </sp:X509Token>
                            </wsp:Policy>
                        </sp:RecipientToken>
                        <sp:AlgorithmSuite>
                            <wsp:Policy>
                                <sp:TripleDesRsa15/>
                            </wsp:Policy>
                        </sp:AlgorithmSuite>
                        <sp:Layout>
                            <wsp:Policy>
                                <sp:Strict/>
                            </wsp:Policy>
                        </sp:Layout>
                        <sp:IncludeTimestamp/>
                        <sp:OnlySignEntireHeadersAndBody/>
                    </wsp:Policy>
                </sp:AsymmetricBinding>
                <sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                        <sp:MustSupportRefKeyIdentifier/>
                        <sp:MustSupportRefIssuerSerial/>
                    </wsp:Policy>
                </sp:Wss10>
                <sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <sp:Body/>
                </sp:SignedParts>
                <sp:EncryptedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <sp:Body/>
                </sp:EncryptedParts>

                <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
                    <ramp:user>server</ramp:user>
                    <ramp:encryptionUser>client</ramp:encryptionUser>
                    <ramp:passwordCallbackClass>crishantha.rampart.PWCBHandler</ramp:passwordCallbackClass>

                    <ramp:signatureCrypto>
                        <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.file">server.jks</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
                        </ramp:crypto>
                    </ramp:signatureCrypto>
                    <ramp:encryptionCypto>
                        <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.file">server.jks</ramp:property>
                            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">password</ramp:property>
                        </ramp:crypto>
                    </ramp:encryptionCypto>
                </ramp:RampartConfig>

            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>

</service>

Now go to the command line and use the jar -cvf TemperatureConversionService.aar * to create the service archive file. This .aar file can be placed in the TOMCAT_HOME/webapps/axis2/WEB-INF/services folder. If the created web service was deployed properly, you can check it with the following link. http://localhost:8080/axis2/services/TemperatureConversionService?wsdl

If you can see the WSDL with the added policy, then you are on the right track.

Creating the Service Client

Step 1: Create a Dynamic Web Project and open up a command line and move to the created project and execute the following command to create relevant stubs for the created web service.

wsdl2java.sh -uri http://localhost:8080/axis2/services/TemperatureConversionService?wsdl -p crishantha.rampart.client -uw -o .

This will create relevant stub class files and related service call back classes.

Step 2: Writing the Password Callback Class

This is similar to the Password Callback class that you wrote at the service side. This is used to extract the private key of the client and the public key of the server. Both of these are extracted from the client keystore.(client.jks) . Here, the “client” is the alias of the public-private key pair and the “server” is the alias of the server public key. In this example, both have the same password, which is “password”.

public class PWCBHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {

        for (int i = 0; i < callbacks.length; i++) {
            WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
            String id = pwcb.getIdentifer();
            if ("client".equals(id)) {
                pwcb.setPassword("password");
            } else if ("server".equals(id)) {
                pwcb.setPassword("password");
            }
        }
    }

}

Step 3: Writing the client

public class TemperatureConversionClient {

	public static void main(String[] args) throws Exception {

		System.out.println("Rampart Sample 03");
    	ConfigurationContext ctx = ConfigurationContextFactory.createConfigurationContextFromFileSystem("/home/crishantha/WSRampart/axis2-1.5.1/repository", null);
    	TemperatureConversionServiceStub stub = new TemperatureConversionServiceStub(ctx,"http://localhost:8888/axis2/services/TemperatureConversionService");
		ServiceClient sc = stub._getServiceClient();
		sc.engageModule("rampart");

		Policy rampartConfig = getRampartConfig();
		sc.getAxisService().getPolicyInclude().addPolicyElement(PolicyInclude.AXIS_SERVICE_POLICY, rampartConfig);

		float farenhitValue = stub.celcius2Farenhit(120);
		System.out.println("Farenhit value for 120 C is : " + farenhitValue);

		float celciusValue = stub.farenhit2Celcius(210);
		System.out.println("Celcius value for 210 F is : " + celciusValue);
	}

	private static Policy getRampartConfig(){

		RampartConfig rampartConfig = new RampartConfig();
		rampartConfig.setUser("client");
		rampartConfig.setEncryptionUser("server");
		rampartConfig.setPwCbClass("crishantha.rampart.client.PWCBHandler");

		CryptoConfig sigCrypto = new CryptoConfig();

		sigCrypto.setProvider("org.apache.ws.security.components.crypto.Merlin");

		Properties props = new Properties();
		props.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", "JKS");
		props.setProperty("org.apache.ws.security.crypto.merlin.file","client.jks");
		props.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", "password");

		sigCrypto.setProp(props);

		rampartConfig.setSigCryptoConfig(sigCrypto);
		rampartConfig.setEncrCryptoConfig(sigCrypto);

		Policy policy = new Policy();
		policy.addAssertion(rampartConfig);

		return policy;
	}
}

Step 4: Creating the client archive (JAR)

Then, you can use the eclipse created build.xml file to create a client JAR file. Just type “ant” by moving to the eclipse working project directory. After creating the JAR, execute the following script.

java -Daxis2.repo=$AXIS2_HOME/repository \
 -Djava.ext.dirs=$AXIS2_HOME/lib:`pwd`/build/lib:$JAVA_HOME/jre/lib/security:$JAVA_HOME/jre/lib/ext \
 crishantha.rampart.client.TemperatureConversionClient

If you have followed all the steps correctly, you should see the output on your console. This could be something like below:

Farenhit value for 120 C is : 152.0
Celcius value for 210 F is : 98.888885

The source code can be found here.

VN:F [1.9.22_1171]
Rating: 6.0/10 (2 votes cast)
VN:F [1.9.22_1171]
Rating: +1 (from 1 vote)

Developing JAX-RS Web Services with Apache CXF

Apache CXF JAX-RS development

In my previous article I have discussed about how Apache CXF can be used to develop JAX-WS web services. In this article I am going to discuss about its JAX-RS support. Along with that, it will discuss the Apache CXF WADL support as well.

The Development Environment

- JDK1.6 or above

- IDE: Eclipse Galelio / Helios / Indigo (JEE Enterprise)

- Application Server: Tomcat 5.5 or above

- JAX-RS Framework: Apache CXF (The download link is http://cxf.apache.org/download.html. Here I have used the CXF 2.4.8)

- MySQL connector for the database connectivity. (Copy the mysql connectivity JAR file to the WEB-INF/lib folder of the Eclipse Project space)

P.NOTE: It is not required to use all JAR files in CXF 2.4.8 lib folder in your development environment. The following selection would be enough.

asm-3.1.jar
jackson-core-asl-1.9.2.jar
jackson-jaxrs-1.9.2.jar
jackson-mapper-asl-1.9.2.jar
jackson-xc-1.9.2.jar
jersey-client-1.12.jar
jersey-core-1.12.jar
jersey-json-1.12.jar
jersey-server-1.12.jar
jersey-servlet-1.12.jar
jettison-1.1.jar
jsr311-api-1.1.1.jar

For Eclipse Galelio Users only …

If you are using Eclipse Galelio, the CXF plugin is not installed as the default feature. It is available only after Helios. Therefore, the Galelio users need to carry out following.

1. Go to Help -> Install New Software on Eclipse Galelio menu. Then in the drop down ‘Work With’ select : The Eclipse Web Tools Platform (WTP) Project update site – http://download.eclipse.org/webtools/updates.

2. In the ‘type filter text’, type in ‘cxf’ and select the SDK and other results and install them.

3. Restart Eclipse and you will get CXF 2.x preferences under Window –> Preferences –> Web Services

Example

Step 1: Create the StudentDB database and StudentDetails table – Please refer the source code.

Step 2: Create the Bean/ POJO class (Student.java) – Please refer the source code.

Step 3: Create the DAO class (StudentDAO.java) – Please refer the source code.

Step 4: Create the interface class (StudentDetails.java)

package crishantha.cxf.service;

import crishantha.cxf.beans.Student;

public interface StudentDetails {
	Student getStudentDetails(String studentId);

}

Step 5: Create the service implementation (StudentDetailsImpl.java)

package crishantha.cxf.service;

import java.sql.SQLException;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import crishantha.cxf.beans.Student;
import crishantha.cxf.storage.StudentDAO;

@Path("/students")
public class StudentDetailsImpl implements StudentDetails {

	@Override
	@GET
	@Consumes({MediaType.APPLICATION_JSON})
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	@Path("{studentId}")
	public Student getStudentDetails(@PathParam("studentId") String studentId) {

		StudentDAO studentDao = new StudentDAO();
	       Student student = null;
	       try {
	            student = studentDao.getStudentDetails(studentId);
	       } catch (SQLException e) {
	           e.printStackTrace();
	       }

	       return student;
	}
}

Step 6: Create the beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxrs

http://cxf.apache.org/schemas/jaxrs.xsd

http://cxf.apache.org/jaxws

	http://cxf.apache.org/schemas/jaxws.xsd">
	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<jaxrs:server id="base" address="/">
		<jaxrs:serviceBeans>
			<ref bean="StudentService" />
		</jaxrs:serviceBeans>
	</jaxrs:server>
	<bean id="StudentService" class="crishantha.cxf.service.StudentDetailsImpl" />
</beans>

Step 7: Change the web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/beans.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

P.NOTE: You are required to manually create beans.xml and change the web.xml mainly because the current CXF Eclipse plugin does not have an automatic code generation for JAX-RS unlike for JAX-WS web services.

Step 8: Create the WAR file and deploy it in the Tomcat server

Right click the Dynamic Web Project (cxf_jaxrs_service) and export it to a WAR file (cxf_jaxrs_service.war) and deploy it to the TOMCAT_HOME/webapps folder. After that do a restart.

Testing

1. Type the following URL on your favorite browser.

http://localhost:8080/cxf_jaxrs_service/services

(Here cxf_jaxrs_service is the JAR-RS web app with the service)

You will see a WADL link for the developed REST service.

Then click the WADL link and you would see the following.

Now you can test the Apache CXF service by typing the following URL.

http://localhost:8080/cxf_jaxrs_service/students/1

(See the following image for more information)

Source Code

The source code can be found here

VN:F [1.9.22_1171]
Rating: 6.8/10 (9 votes cast)
VN:F [1.9.22_1171]
Rating: +8 (from 10 votes)

Developing JAX-WS Web Services with Apache CXF

Introduction to Apache CXF

In my previous blogs, I was mostly using the popular web service framework Axis2. However from its inception in the open source space, Axis2 was mostly compared by Apache CXF web service framework (Formerly XFire).  There are plenty of articles you may find on Internet related to this subject and me too planning to dig further into it in future as well.

Here, my main objective is to introduce one more web service framework to the audience in my series of web services articles. I hope you will enjoy them as much as I do.

The Development Environment

JDK1.6 or above

IDE: Eclipse Galelio / Helios / Indigo (JEE Enterprise)

Application Server: Tomcat 5.5 or above

JAX-WS Framework: Apache CXF (The download link is http://cxf.apache.org/download.html. Here I have used the CXF 2.4.8)

MySQL connector for the database connectivity. (Copy the mysql connectivity JAR file to CXF downloaded lib folder. Then it will be automatically added to the Eclipse workspace)

For Eclipse Galelio Users only …

If you are using Eclipse Galelio, the CXF plugin is not installed as the default feature. It is available only after Helios. Therefore, the Galelio users need to carry out following.

1. Go to Help -> Install New Software on Eclipse Galelio menu. Then in the drop down ‘Work With’ select : The Eclipse Web Tools Platform (WTP) Project update site – http://download.eclipse.org/webtools/updates.

2. In the ‘type filter text’, type in ‘cxf’ and select the SDK and other results and install them.

3. Restart Eclipse and you will get CXF 2.xPreferences under Window –> Preferences –> Web Services

Setting Up the Environment

1. Open up Eclipse and create a “Dynamic Web Project” – “cxf_jaxws_service”

2. Make sure Java and Apache Tomcat is properly configured in your Eclipse workspace. Just check the Window –> Preferences option to check those. Additionally you need to make sure Apache CXF runtime is configured properly to the Eclipse workspace. For that Check Window -> Preferences –> Web Services –> CXF 2.x Preferences. Here you need to set the CXF Home folder properly. This will add the CXF libraries to the Project workspace.

Now the environment is fully setup to test out Apache CXF JAX-WS features.

The Example

This is a simple example to show how to configure Apache CXF to retrieve Student Details.

Step 1: Create the StudentDB and the StudetDetails table by running the script attached to the source code.

Step 2: Create the bean class (Student.java)

@XmlRootElement
public class Student {

 private int studentID;
 private String studentName;
 private String studentAddress;
 private String studentTelNo;

 public Student() {}

 public Student(int id, String name, String address, String telno) {
   this.studentID = id;
   this.studentName = name;
   this.studentAddress = address;
   this.studentTelNo = telno;
 }

 public int getStudentID() {
   return studentID;
 }

 public void setStudentID(int studentID) {
   this.studentID = studentID;
 }

 public String getStudentName() {
   return studentName;
 }

 public void setStudentName(String studentName) {
   this.studentName = studentName;
 }

 public String getStudentAddress() {
   return studentAddress;
 }

 public void setStudentAddress(String studentAddress) {
   this.studentAddress = studentAddress;
 }

 public String getStudentTelNo() {
   return studentTelNo;
 }

 public void setStudentTelNo(String studentTelNo) {
   this.studentTelNo = studentTelNo;
 }
}

Step 3: Create the DAO Class (StudentDAO.java)

public class StudentDAO {

    Connection connection = null;
    Statement statement = null;
    ResultSet rs = null;

    public Student getStudentDetails(String studentID) throws SQLException {
        String studentName = null;
        String studentAddress = null;
        String studentTelNo = null;

        connection = getConnection();
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
        }

        String sqlStatement = "select * from StudentDetails where studentID = " + studentID + "";
        rs = statement.executeQuery(sqlStatement);

        Student student = null;
        while (rs.next()) {
            student = new Student();
            studentName = rs.getString("studentName");
            studentAddress = rs.getString("studentAddress");
            studentTelNo = rs.getString("studentTelNo");

            student.setStudentName(studentName);
            student.setStudentAddress(studentAddress);
            student.setStudentTelNo(studentTelNo);
        }

        statement.close();
        connection.close();

        return student;
    }

    private Connection getConnection() {

    	String driverName = "com.mysql.jdbc.Driver";
        String conectionURI = "jdbc:mysql://localhost:3306/StudentDB";
        String userName = "root";
        String password = "";

        try {
            Class.forName(driverName);

            try {
                connection = DriverManager.getConnection(conectionURI, userName, password);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                connection.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return connection;
    }
}

Step 4: Create the JAX-WS Web Service (StudentDetails.java)

package crishantha.cxf.service;

import java.sql.SQLException;

import crishantha.cxf.beans.Student;
import crishantha.cxf.storage.StudentDAO;

public class StudentDetails {

	public Student getStudentDetails(String studentId) {

	       StudentDAO studentDao = new StudentDAO();
	       Student student = null;
	       try {
	            student = studentDao.getStudentDetails(studentId);
	       } catch (SQLException e) {
	           e.printStackTrace();
	       }

	       return student;
	}
}

- Once you create the StudentDetails.java right click the class and select WebServices –> Create Web Service option.- Then you will be prompted with the usual web service creation screen on Eclipse. Here however unlike in Axis2, you are required to change the Web Services Runtime to Apache CXF 2.x. (Under the Service Implementation).

- Then on the same screen it is easy to create the Client project as well by moving up the progress bar to the “Deploy Client”. This will create both the service skeleton and the client stubs as well.

- Select the default options of next screens, if you do not need any changes. That will create Wrapper and Fault bean classes, WSDL and XSDs.

- After completing the above process, you will see a modified service class with JAX-WS annotations.

package crishantha.cxf.service;

import java.sql.SQLException;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

import crishantha.cxf.beans.Student;
import crishantha.cxf.storage.StudentDAO;

@WebService(targetNamespace="http://service.cxf.crishantha/", portName="StudentDetailsPort", serviceName="StudentDetailsService")
public class StudentDetails {

	@WebMethod(operationName="getStudentDetails", action="urn:GetStudentDetails")
	public Student getStudentDetails(@WebParam(name="arg0") String studentId) {

	       StudentDAO studentDao = new StudentDAO();
	       Student student = null;
	       try {
	            student = studentDao.getStudentDetails(studentId);
	       } catch (SQLException e) {
	           e.printStackTrace();
	       }

	       return student;
	}
}
- If you had selected the “Deploy Client” that will create a new Client Dynamic Web Project in your workspace as well. That will most probably be “cxf_jaxws_serviceClient” in this scenario.
Step 5: If everything, had gone according to that plan, by typing the following WSDL URL you should be able to see the web service WSDL.

http://localhost:8080/cxf_service_02/services/StudentDetailsPort?wsdl

Step 6: As I mentioned above, the client web application also would have created on the same workspace if you had noticed before. Go inside the src folder and you can create a client leveraging the generated stub classes. Using this you can easily test the created web service.

package crishantha.cxf.service;

import java.net.MalformedURLException;
import java.net.URL;

public class MyClient {

	public static void main(String[] args) {
		StudentDetailsService ss = null;
		try {
			ss = new StudentDetailsService(new URL("http://localhost:8080/cxf_service_02/services/StudentDetailsPort?wsdl"));
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		StudentDetails service = ss.getStudentDetailsPort();
		Student student = service.getStudentDetails("5");

		System.out.println("Student Name : " + student.getStudentName());
		System.out.println("Student Address : " + student.getStudentAddress());
		System.out.println("Student Name : " + student.getStudentTelNo());
	}

}

Source Code

The source code can be found here

VN:F [1.9.22_1171]
Rating: 6.5/10 (2 votes cast)
VN:F [1.9.22_1171]
Rating: +5 (from 5 votes)

Developing RESTful web services with Apache Wink

Introduction to Apache Wink

As I mentioned in my previous post, Apache Wink is another JSR 311 (JAX-RS) reference implementation. You are able to download the Apache Wink binaries from the link http://incubator.apache.org/wink.

The Development Environment

JDK1.6 or above

IDE: Eclipse Galelio (Enterprise)

Application Server: Tomcat 5.5

JAX-RS Framework: Apache Wink. (The downloaded location will be referred as WINK_HOME in this tutorial)

MySQL connector for the database connectivity.

Setting Up the Environment

1. Open up Eclipse and create a “Dynamic Web Project” – “jersey_service_student”

2. Make sure Java and Apache Tomcat is properly configured in your Eclipse workspace. Just check the Window –> Preferences option to check those.

3. Copy all jar files from WINK_HOME/lib and WINK_HOME/dist folders to WEB-INF/lib folder. Additionally it is required to copy MySQL connector JAR file also to WEB-INF/lib folder for the database connectivity.

Thats it! Now you are good to go!

The Example

This is a simple example to show how to configure Apache Wink to retrieve Student Details from a database.

Step 1: Create the Student database (StudentDB) and the StudentsDetails table with some records added to it. See the source code for the database script.
Step 2: Modify the web.xml to map with Apache Wink Framework.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Wink REST Service</display-name>
  <servlet>
    <servlet-name>StudentServlet</servlet-name>
    <servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
    <init-param>
      <param-name>javax.ws.rs.Application</param-name>
      <param-value>crishantha.wink.resources.StudentResourceApplication</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>StudentServlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>
Step 3: Define Resources

StudentResourceApplication.java

package crishantha.wink.resources;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class StudentResourceApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(StudentResource.class);

        return classes;
    }
}

StudentResource.java

package crishantha.wink.resources;

import java.sql.SQLException;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import crishantha.wink.storage.StudentDAO;
import crishantha.wink.beans.Student;

@Path("/students")
public class StudentResource {

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public List<Student> getStudents() {
        StudentDAO studentDao = new StudentDAO();
        List<Student> students = null;

        try {
            students = studentDao.getStudents();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return students;
    }

    @GET
    @Path("{studentid}")
    @Produces(MediaType.APPLICATION_XML)
    public Student getStudentName(@PathParam("studentid") int studentID) {
        StudentDAO studentDao = new StudentDAO();
        Student student = null;
        try {
            student = studentDao.getStudentDetails(studentID);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return student;
    }
}

Step 4: Define the bean classes

The related bean class (Student.java) is given below.

Student.java

package crishantha.wink.beans;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Student {

 private int studentID;
 private String studentName;
 private String studentAddress;
 private String studentTelNo;

 public Student() {}

 public Student(int id, String name, String address, String telno) {
   this.studentID = id;
   this.studentName = name;
   this.studentAddress = address;
   this.studentTelNo = telno;
 }

 public int getStudentID() {
   return studentID;
 }

 public void setStudentID(int studentID) {
   this.studentID = studentID;
 }

 public String getStudentName() {
   return studentName;
 }

 public void setStudentName(String studentName) {
   this.studentName = studentName;
 }

 public String getStudentAddress() {
   return studentAddress;
 }

Step 5: Define the Data Access Layer

The Data Access Layer classes are given below. These are used to implement the data access layer. Here the simple SQL is used for GET requests. The other REST operations are not specified here for brevity.

StudentDAO.java

package crishantha.wink.storage;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

import crishantha.wink.beans.Student;

public class StudentDAO {

    Connection connection = null;
    Statement statement = null;
    ResultSet rs = null;

    public Student getStudentDetails(int studentID) throws SQLException {
        String studentName = null;
        String studentAddress = null;
        String studentTelNo = null;

        connection = getConnection();
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
        }

        String sqlStatement = "select * from StudentDetails where studentID = " + studentID + "";
        rs = statement.executeQuery(sqlStatement);

        Student student = null;
        while (rs.next()) {
            student = new Student();
            studentName = rs.getString("studentName");
            studentAddress = rs.getString("studentAddress");
            studentTelNo = rs.getString("studentTelNo");

            student.setStudentName(studentName);
            student.setStudentAddress(studentAddress);
            student.setStudentTelNo(studentTelNo);
        }

        statement.close();
        connection.close();

        return student;
    }

    public List<Student> getStudents() throws SQLException {
        String studentName = null;
        String studentAddress = null;
        String studentTelNo = null;
        int studentID = 0;

        connection = getConnection();
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
        }

        String sqlStatement = "Select * from StudentDetails";
        rs = statement.executeQuery(sqlStatement);

        List<Student> students= new ArrayList<Student>();

        while (rs.next()) {
            Student student = new Student();

            studentID = rs.getInt("studentID");
            studentName = rs.getString("studentName");
            studentAddress = rs.getString("studentAddress");
            studentTelNo = rs.getString("studentTelNo");

            student.setStudentID(studentID);
            student.setStudentName(studentName);
            student.setStudentAddress(studentAddress);
            student.setStudentTelNo(studentTelNo);

            students.add(student);
        }

        statement.close();
        connection.close();

        return students;
    }

    private Connection getConnection() {

    	String driverName = "com.mysql.jdbc.Driver";
        String conectionURI = "jdbc:mysql://localhost:3306/StudentDB";
        String userName = "root";
        String password = "";

        try {
            Class.forName(driverName);

            try {
                connection = DriverManager.getConnection(conectionURI, userName, password);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                connection.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return connection;
    }
}

So now you have completed the coding. Time to test!

Step 6: Deploy the Apache Wink Application to a web container (Tomcat).

This is simple, Just right click the Eclipse project and Select Export –> WAR file and specify the correct WAR file name and specify the location to be deployed. You can directly specify the webapps folder of the Tomcat engine that you wish to deploy this application. Restart the Tomcat server. Lets assume that the WAR file name as wink_service_01.war.

Step 7: Testing the Application

For this you can either use a simple browser or any other tool to carry out this task. If I use a normal browser to test this,
To list all the student details, –> http://localhost:8080/wink_service_01/rest/students
To list the student with StudentID “1″–> http://localhost:8080/wink_service_01/rest/students/1

The Source Code

The Source Code is available here

VN:F [1.9.22_1171]
Rating: 10.0/10 (2 votes cast)
VN:F [1.9.22_1171]
Rating: +4 (from 4 votes)

Developing RESTful Web Services with Jersey

Introduction to JAX-RS implementations

With the introduction of JSR 311 (JAX-RS) specification in 2008, there have been a few reference implementations, which became popular to develop RESTful Java based services. The popular ones, which facilitate the JAX-RS specification are,

1. Jersey (http://jersey.java.net/)

2. Apache Wink (http://incubator.apache.org/wink/)

3. Apache CXF (http://cxf.apache.org/)

4. RESTEasy (http://www.jboss.org/resteasy/)

5. RESTlets (http://www.restlet.org)

As a start, in this tutorial will talk about Jersey reference implementation in detail.

The Development Environment

JDK1.6 or above

IDE: Eclipse Galelio (Enterprise)

Application Server: Tomcat 5.5

JAX-RS Framework: Jersey Framework (You can download the binaries from http://jersey.java.net/)

Setting Up the Environment

1. Open up Eclipse and create a “Dynamic Web Project” – “jersey_service_student”

2. Make sure Java and Apache Tomcat is properly configured in your Eclipse workspace. Just check the Window –> Preferences option to check those.

3. Copy all jar files from the Jersey binary download to the WEB-INF/lib folder within the created Eclipse project. Additionally it is required to copy MySQL connector JAR file also to WEB-INF/lib folder for the database connectivity.

Thats it! Now you are good to go!

The Example

This is a simple example to show how to configure Jersey to retrieve Student Details from a database.

Step 1: Create the Student database (StudentDB) and the StudentsDetails table with some records added to it. See the source code for the database script.
Step 2: Modify the web.xml to map with Jersey Framework.
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Jersey</display-name>

  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>crishantha.jersey.resources</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

In the above web.xml, a servlet dispatcher (com.sun.jersey.spi.container.servlet.ServletContainer) is created to allow all REST requests to come in and it defines the java package of the resources as a parameter.

Step 3: Define Resources

StudentsResource.java

@Path("/students")
public class StudentsResource {

	@Context
	UriInfo uriInfo;
	@Context
	Request request;

	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public List<Student> getStudents() {
		List<Student> students = new ArrayList<Student>();
		students.addAll(StudentStore.getStore().values());

        return students;
	}

	@GET
	@Path("count")
	@Produces(MediaType.TEXT_PLAIN)
	public String getCount() {
		int count = StudentStore.getStore().size();
		return String.valueOf(count);
	}

	@Path("{student}")
	public StudentResource getStudent(
			@PathParam("student") String student) {
		return new StudentResource(uriInfo, request, student);
	}
}

StudentResource.java

public class StudentResource {
	@Context
	UriInfo uriInfo;
	@Context
	Request request;
	String studentID;

	public StudentResource(UriInfo uriInfo, Request request, String studentID) {
		this.uriInfo = uriInfo;
		this.request = request;
		this.studentID = studentID;
	}

	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
          public Student getStudent() {
            StudentDAO studentDao = new StudentDAO();
            Student student = null;
            try {
              student = studentDao.getStudentDetails(studentID);
            } catch (SQLException e) {
              e.printStackTrace();
            }
	    if(student==null)
		throw new NotFoundException("The Student not Found");
            return student;
        }
}
Step 4: Define the bean classes

The related bean class (Student.java) is given below.

Student.java

@XmlRootElement
public class Student {

    private int studentID;
	private String studentName;
    private String studentAddress;
    private String studentTelNo;

	public Student() {}

	public Student(int id, String name, String address, String telno) {
		this.studentID = id;
		this.studentName = name;
		this.studentAddress = address;
		this.studentTelNo = telno;
	}

        public int getStudentID() {
		return studentID;
	}

	public void setStudentID(int studentID) {
		this.studentID = studentID;
	}

	public String getStudentName() {
		return studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
Step 5: Define the Data Access Layer

The respective StudentStore.java and StudentDAO.java classes are as follows. These are used to implement the data access layer. Here the simple SQL is used for GET requests. The other REST operations are not specified here for brevity.

StudentDAO.java

public class StudentDAO {

    Connection connection = null;
    Statement statement = null;
    ResultSet rs = null;

    public Student getStudentDetails(String studentID) throws SQLException {
        String studentName = null;
        String studentAddress = null;
        String studentTelNo = null;

        connection = getConnection();
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
        }

        String sqlStatement = "select * from StudentDetails where studentID = " + studentID + "";
        rs = statement.executeQuery(sqlStatement);

        Student student = null;
        while (rs.next()) {
            student = new Student();
            studentName = rs.getString("studentName");
            studentAddress = rs.getString("studentAddress");
            studentTelNo = rs.getString("studentTelNo");

            student.setStudentName(studentName);
            student.setStudentAddress(studentAddress);
            student.setStudentTelNo(studentTelNo);
        }

        statement.close();
        connection.close();

        return student;
    }

    public List<Student> getStudents() throws SQLException {
        String studentName = null;
        String studentAddress = null;
        String studentTelNo = null;
        int studentID = 0;

        connection = getConnection();
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
        }

        String sqlStatement = "Select * from StudentDetails";
        rs = statement.executeQuery(sqlStatement);

        List<Student> students= new ArrayList<Student>();

        while (rs.next()) {
            Student student = new Student();

            studentID = rs.getInt("studentID");
            studentName = rs.getString("studentName");
            studentAddress = rs.getString("studentAddress");
            studentTelNo = rs.getString("studentTelNo");

            student.setStudentID(studentID);
            student.setStudentName(studentName);
            student.setStudentAddress(studentAddress);
            student.setStudentTelNo(studentTelNo);

            students.add(student);
        }

        statement.close();
        connection.close();

        return students;
    }

   private Connection getConnection() {

    	String driverName = "com.mysql.jdbc.Driver";
        String conectionURI = "jdbc:mysql://localhost:3306/StudentDB";
        String userName = "root";
        String password = "";

        try {
            Class.forName(driverName);

            try {
                connection = DriverManager.getConnection(conectionURI, userName, password);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                connection.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return connection;
    }

So now you have completed the coding. Time to test!

Step 6: Deploy the Jersey Application to a web container (Tomcat).

This is simple, Just right click the Eclipse project and Select Export –> WAR file and specify the correct WAR file name and specify the location to be deployed. You can directly specify the webapps folder of the Tomcat engine that you wish to deploy this application. Restart the Tomcat server. Lets assume that the WAR file name as Jsersey_service_01.war.

Step 7: Testing the Application

For this you can either use a simple browser or any other tool to carry out this task. If I use a normal browser to test this,
To list all the student details, –> http://localhost:8080/Jersey_service_01/rest/students
To list the student with StudentID “1″–> http://localhost:8080/Jersey_service_01/rest/students/1

To list the total count of all students –> http://localhost:8080/Jersey_service_01/rest/students/count

The Source Code

The Source Code is available here

VN:F [1.9.22_1171]
Rating: 10.0/10 (1 vote cast)
VN:F [1.9.22_1171]
Rating: +3 (from 3 votes)
Go to Top