Thursday, April 24, 2014

Create a client for WSO2 Admin Service

WSO2 provide a middleware stack that can be used to fulfill any middleware requirement. All the functionality can be controlled via the management console. There is a specific web service created for every feature in the product stack. Normally every component is consist of a back end component, a service stub and a front end component. Front end component is a client for the back end web service. These web services are called Admin Services.
If you get a WSO2 Identity Server, normally the WSDL of these Admin Services are hidden. You can go to IS_HOME/repository/conf/carbon.xml and change HideAdminServiceWSDLs to false (restart the server if it is already running), you can see the WSDL if your required Admin Service. For example you will be able to see the WSDL of the below admin service.

https://localhost:9443/services/StatisticsAdmin?wsdl

Now we are going to create a client for the above mentioned admin service. First create a maven project. Then add the following repository to the pom.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<repositories>
 <repository>
  <id>wso2-nexus</id>
  <name>WSO2 internal Repository</name>
  <url>http://maven.wso2.org/nexus/content/groups/wso2-public/</url>
  <releases>
   <enabled>true</enabled>
   <updatePolicy>daily</updatePolicy>
   <checksumPolicy>ignore</checksumPolicy>
  </releases>
 </repository>
</repositories>

<pluginRepositories>
 <pluginRepository>
  <id>central</id>
  <url>http://repo1.maven.org/maven2</url>
 </pluginRepository>
 <pluginRepository>
  <id>wso2-maven2-repository-1</id>
  <url>http://dist.wso2.org/maven2</url>
 </pluginRepository>
 <pluginRepository>
  <id>wso2-maven2-repository-2</id>
  <url>http://dist.wso2.org/snapshots/maven2</url>
 </pluginRepository>
</pluginRepositories>

Then add the following dependencies.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>3.8.1</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.wso2.carbon</groupId>
  <artifactId>org.wso2.carbon.authenticator.stub</artifactId>
  <version>4.0.0</version>
 </dependency>
 <dependency>
  <groupId>org.apache.axis2.wso2</groupId>
  <artifactId>axis2-client</artifactId>
  <version>1.6.1.wso2v10</version>
 </dependency>
 <dependency>
  <groupId>org.wso2.carbon</groupId>
  <artifactId>org.wso2.carbon.statistics.stub</artifactId>
  <version>4.0.0</version>
 </dependency>
</dependencies>

To get the service from a admin service first we need to authenticate. To do this there is a separate admin service.

Following code will handle the login process and will return a cookie.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import org.apache.axis2.AxisFault; 
import org.apache.axis2.transport.http.HTTPConstants; 
import org.wso2.carbon.authenticator.stub.AuthenticationAdminStub; 
import org.wso2.carbon.authenticator.stub.LoginAuthenticationExceptionException; 
import org.wso2.carbon.authenticator.stub.LogoutAuthenticationExceptionException; 
import org.apache.axis2.context.ServiceContext; 
import java.rmi.RemoteException; 

public class LoginAdminServiceClient { 
  private final String serviceName = "AuthenticationAdmin"; 
    private AuthenticationAdminStub authenticationAdminStub; 
    private String endPoint; 

    public LoginAdminServiceClient(String backEndUrl) throws AxisFault { 
      this.endPoint = backEndUrl + "/services/" + serviceName; 
      authenticationAdminStub = new AuthenticationAdminStub(endPoint); 
    } 

    public String authenticate(String userName, String password) throws RemoteException, 
                                      LoginAuthenticationExceptionException { 

      String sessionCookie = null; 

      if (authenticationAdminStub.login(userName, password, "localhost")) { 
        System.out.println("Login Successful"); 

        ServiceContext serviceContext = authenticationAdminStub. 
            _getServiceClient().getLastOperationContext().getServiceContext(); 
        sessionCookie = (String) serviceContext.getProperty(HTTPConstants.COOKIE_STRING); 
        System.out.println(sessionCookie); 
      } 

      return sessionCookie; 
    } 

    public void logOut() throws RemoteException, LogoutAuthenticationExceptionException { 
      authenticationAdminStub.logout(); 
      System.out.println("Logout");
    } 
}

Now we can access the required admin service. Following class will handle the necessary steps.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.apache.axis2.AxisFault; 
import org.apache.axis2.client.Options; 
import org.apache.axis2.client.ServiceClient;
import org.wso2.carbon.statistics.stub.StatisticsAdminStub;
import org.wso2.carbon.statistics.stub.types.carbon.SystemStatistics;

import java.rmi.RemoteException; 
  
 public class StatisticsAdminClient { 
   private final String serviceName = "StatisticsAdmin"; 
   private StatisticsAdminStub statisticsAdminStub; 
   private String endPoint; 
  
   public StatisticsAdminClient(String backEndUrl, String sessionCookie) throws AxisFault { 
     this.endPoint = backEndUrl + "/services/" + serviceName; 
     statisticsAdminStub = new StatisticsAdminStub(endPoint); 
     //Authenticate Your stub from sessionCooke 
     ServiceClient serviceClient; 
     Options option; 
  
     serviceClient = statisticsAdminStub._getServiceClient(); 
     option = serviceClient.getOptions(); 
     option.setManageSession(true); 
     option.setProperty(org.apache.axis2.transport.http.HTTPConstants.COOKIE_STRING, sessionCookie); 
   } 
  
   public SystemStatistics getSystemStatistics() throws RemoteException { 
     return statisticsAdminStub.getSystemStatistics(); 
   } 
 }

To access the required admin service we need to present the obtained cookie. But you will face some problems as the jvm does not accept the security certificates presented by the server. So we need to tell the jvm that the security certificate presented by the server is trusted. We can do this by setting a system property. You will need to change the property to point to the correct jks file in the server.
You can change the backEndUrl to point to the correct server url.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.wso2.carbon.authenticator.stub.LoginAuthenticationExceptionException;
import org.wso2.carbon.authenticator.stub.LogoutAuthenticationExceptionException;
import org.wso2.carbon.identity.entitlement.stub.EntitlementServiceException;
import org.wso2.carbon.statistics.stub.types.carbon.SystemStatistics;
import java.rmi.RemoteException;

public class GetSystemStatistics {
 public static void main(String[] args)
   throws LoginAuthenticationExceptionException,
   LogoutAuthenticationExceptionException,
   EntitlementServiceException, RemoteException{
  System.setProperty(
    "javax.net.ssl.trustStore",
    "/home/maduranga/WSO2/IS/21-04-2014/wso2is-5.0.0/repository/resources/security/wso2carbon.jks");
  System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
  System.setProperty("javax.net.ssl.t" + "rustStoreType", "JKS");
  String backEndUrl = "https://localhost:9443";

  LoginAdminServiceClient login = new LoginAdminServiceClient(backEndUrl);
  String session = login.authenticate("admin", "admin");
  StatisticsAdminClient statisticsAdminClient = new StatisticsAdminClient(
    backEndUrl, session);
  SystemStatistics stat = statisticsAdminClient.getSystemStatistics();

  System.out.println("Host :" + stat.getServerName());
  System.out.println("Server Start Time :" + stat.getServerStartTime());
  System.out.println("System Up Time :" + stat.getSystemUpTime());
  System.out.print(stat.getUsedMemory().getValue()
    + stat.getUsedMemory().getUnit() + " of ");
  System.out.println(stat.getTotalMemory().getValue()
    + stat.getTotalMemory().getUnit() + " memory used");

  login.logOut();
 }
}

Now you can see the statistics of the server. By doing necessary changes you can access any admin service in the server.

Tuesday, April 15, 2014

Serialize and de-serialize objects with JAXB

Java Architecture for XML Binding (JAXB) is used to serialize java objects to XML (marshal) and to de-serialize XML back to java object (unmarshal). Here I am going to demonstrate how to marshal and unmarshal a simple java object using two methods.

First method is for the classes that some annotations can be added. Bellow is the class we are using to make objects which we are going to serialize.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "student")
class Student {
 private String name;
 private int age;
 
 public Student() {
 }

  public void setName(String name) {
  this.name = name;
 }

  public void setAge(int age) {
  this.age = age;
 }

  public String getName() {
  return name;
 }

  public int getAge() {
  return age;
 }
}

As you can see, a annotation is added to tell that this is a XML root element, which is used in serializing the object.

From the bellow method, you can serialize a object created by the above class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public String marshal(Student student){
  StringWriter writer = new StringWriter();
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Marshaller m = context.createMarshaller();
   m.marshal(student, writer);
   return writer.toString();
   
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

This method will return a XML representation of a student object. Bellow method will return the Student object back from a serialized student object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static Student unMarshal(String input){
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Unmarshaller m = context.createUnmarshaller();
   return (Student)m.unmarshal(new StringReader(input));
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

But if you want to serialize a object of which class signature cannot be changed and it does not have the annotations provided in the class above. Lets see how to use JAXB with the objects of a class like bellow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Student {
 private String name;
 private int age;
 
 public Student() {
 }

  public void setName(String name) {
  this.name = name;
 }

  public void setAge(int age) {
  this.age = age;
 }

  public String getName() {
  return name;
 }

  public int getAge() {
  return age;
 }
}

Bellow code can be used to serialize a Student object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public String marshal(Student student){
  StringWriter writer = new StringWriter();
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Marshaller m = context.createMarshaller();
   
         m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         JAXBElement<Student> rootElement = new JAXBElement<Student>(new QName("student"), Student.class, student);
   m.marshal(rootElement, writer);
   return writer.toString();
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

It will return a XML string as in the previous case. And the bellow code can be used to de-serialize a student object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public Student unMarshal(String input){
  JAXBContext context;
  try {
   context = JAXBContext.newInstance(Student.class);
   Unmarshaller m = context.createUnmarshaller();
   StreamSource source = new StreamSource(new StringReader(input));
         return m.unmarshal(source, Student.class).getValue();
  } catch (JAXBException e) {
   e.printStackTrace();
   return null;
  }
}

In this method we have provided appropriate JAXB mapped class to hold node's XML data to the unmarshal method. As the unmarshal method which accepts a class parameter does not accept a just a StringReader, we have made a StreamSource using that StringReader. You can will see that there are lots of unmarshal methods overloaded and you will have to use a suitable one according to your requirements.