Thursday, February 17, 2011

Configuring your Java web application as root (/)

By default, if you deploy an application "myWebApp" in an application server like Glassfish, the root URL is http://my.server.com:8080/myWebApp. To change this, you can use the sun-web.xml descriptor file of the application to configure the application as root application of the server. Which means that the application is accessible using the URL http://my.server.com:8080/.

Inside the sun-web.xml file, make sure the context-root element looks like this:

<context-root>/</context-root>


The document type definition (DTD) of the descriptor file can be found here.

If the web application is part of an EAR-file, make sure you configure this in application.xml. Otherwise, the setting in application.xml will override it!

To make the URL even simpler, you can ofcourse change the listen port to 80. The URL to the application will then look like this: http://my.server.com/

Wednesday, February 16, 2011

Listing Open Source Web Frameworks in Java

I've stumbled acros a nice listing of available open source web frameworks for Java:

http://java-source.net/open-source/web-frameworks.

Are there really too many Java web frameworks?

Monday, February 14, 2011

When @SchemaValidation in JAX-WS web services fails

I'm currently developing web services in an Oracle WebLogic environment. To enable schema validation on the web service, one only has to use the @SchemaValidation annotation in the web service class. Like this:


import com.sun.xml.ws.developer.SchemaValidation;

@WebService(...)
@SchemaValidation
public class WebService implements WebServiceInterface {
...
}


Unfortunately, that doesn't work for me. I get this error when I try to deploy my application:


Caused by: javax.xml.ws.WebServiceException:
Annotation@com.sun.xml.internal.ws.developer.SchemaValidation
(handler=class com.sun.xml.internal.ws.server.DraconianValidationErrorHandler)
is not recognizable,
atleast one constructor of class com.sun.xml.internal.ws.developer.SchemaValidationFeature
should be marked with @FeatureConstructor


I also tried creating my own validator to use in the annotation (@SchemaValidation(handler = ExampleValidationHandler.class)):


import org.xml.sax.SAXParseException;

import com.sun.xml.internal.ws.developer.ValidationErrorHandler;

public class ExampleValidationHandler extends ValidationErrorHandler {

public void warning(SAXParseException exception) {
;
}

public void error(SAXParseException exception) {
;
}

public void fatalError(SAXParseException exception) {
;
}
}


Then I enabled schema validation explicitly in the Oracle WebLogic web services deployment descriptor: weblogic-webservices.xml.


<?xml version="1.0" encoding="UTF-8"?>
<weblogic-webservices
xmlns="http://xmlns.oracle.com/weblogic/weblogic-webservices"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-webservices http://xmlns.oracle.com/weblogic/weblogic-webservices/1.1/weblogic-webservices.xsd">

<webservice-description>
<webservice-description-name>ExampleWSService</webservice-description-name>
<webservice-type>JAXWS</webservice-type>
<port-component>
<port-component-name>ExampleWSService</port-component-name>
<validate-request>true</validate-request>
<reliability-config>
<inactivity-timeout>P0DT600S</inactivity-timeout>
<base-retransmission-interval>P0DT3S</base-retransmission-interval>
<retransmission-exponential-backoff>true</retransmission-exponential-backoff>
<acknowledgement-interval>P0DT3S</acknowledgement-interval>
<sequence-expiration>P1D</sequence-expiration>
<buffer-retry-count>3</buffer-retry-count>
<buffer-retry-delay>P0DT5S</buffer-retry-delay>
</reliability-config>
</port-component>
</webservice-description>
</weblogic-webservices>


Unfortunately, that didn't work either. Google shows me three other developers with the same problem, but there is no working solution for me, and for them. So, another approach is needed to enable schema validation on incoming messages.

The solution presented in this blog post makes use of web service handlers. A web service handler is like an intercepting filter, which enables you to intercept incoming and outgoing messages, before they are passed on to the (external) system. The steps needed for this solution:

1. Create web service handler class
2. Define handler chain in XML-descriptor
3. Use annotation in web service class

1. Create web service handler class

There are two types of web service handlers:
- LogicalHandler
- SOAPHandler

The LogicalHandler provides access to the logical message, without protocol details like: SOAP envelopes. The SOAPHandler provides access to SOAP protocol details. For the purpose of message validation, the LogicalHandler is a better choice.


package com.javaeenotes;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class ValidationHandler implements LogicalHandler<LogicalMessageContext> {
private String schemaUrl = "http://127.0.0.1:8080/webservice/schema.xsd";

@Override
public boolean handleMessage(LogicalMessageContext context) {
Boolean isOutBound = (Boolean) context
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

if (isOutBound) {
return true;
}

LogicalMessage lm = context.getMessage();
Source payload = lm.getPayload();
StreamResult res = new StreamResult(new StringWriter());
String message = "";

try {
Transformer trans;
trans = TransformerFactory.newInstance().newTransformer();
trans.transform(payload, res);
message = res.getWriter().toString();
// Validate
validate(message, schemaUrl);
} catch (TransformerConfigurationException e) {
// When Source payload Transformer object could not be obtained.
throw new WebServiceException(e);
} catch (TransformerFactoryConfigurationError e) {
// When Source payload Transformer object could not be obtained.
throw new WebServiceException(e);
} catch (TransformerException e) {
// When Source payload could not be transformed to String.
throw new WebServiceException(e);
} catch (MalformedURLException e) {
// When URL to schema is invalid.
throw new WebServiceException(e);
} catch (ParserConfigurationException e) {
// When parser needed for validation could not be obtained.
throw new WebServiceException(e);
} catch (IOException e) {
// When something is wrong with IO.
throw new WebServiceException(e);
} catch (SAXException e) {
// When XSD-schema validation fails.
throw new WebServiceException(e);
}

return true;
}

private void validate(String xml, String schemaUrl)
throws ParserConfigurationException, IOException,
MalformedURLException, SAXException {

DocumentBuilder parser = null;
Document document = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
parser = dbf.newDocumentBuilder();

byte bytes[] = xml.getBytes();
document = parser.parse(new ByteArrayInputStream(bytes));
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

Schema schema = null;
schema = factory.newSchema(new URL(schemaUrl));
javax.xml.validation.Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
}

@Override
public boolean handleFault(LogicalMessageContext context) {
return true;
}

@Override
public void close(MessageContext context) {
;
}

}


It is important to note that the payload must be defined in an XSD-schema. A definition in a WSDL file cannot be used. Use the boolean property: MessageContext.MESSAGE_OUTBOUND_PROPERTY to differentiate between out-bound or in-bound messages. Throw a WebServiceException when something goes wrong.

2. Define handler chain in XML-descriptor

Now, create a handler chain XML-descriptor file, which refers to the handler created in the previous step. In this example, I named the file handlers.xml and placed it in the same directory as the handler. The location of the file is important in the last step.


<?xml version="1.0" encoding="UTF-8"?>
<handler-chains 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/javaee_web_services_metadata_handler_2_0.xsd">
<handler-chain>
<handler>
<handler-name>ValidationHandler</handler-name>
<handler-class>com.javaeenotes.ValidationHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>


3. Use annotation in web service class

The final step is to use the @HandlerChain annotation in the web service class. The file parameter must be a relative path to the XML-descriptor file from the annotated class file or an absolute URL.


package com.javaeenotes;

import javax.jws.HandlerChain;
import javax.jws.WebService;

import com.javaeenotes.ws.ExampleWS;
//import com.sun.xml.ws.developer.SchemaValidation;

@WebService(name = "ERSService",
targetNamespace = "http://javaeenotes.com/",
serviceName = "ExampleWSService",
portName = "ExampleWSPort",
endpointInterface = "com.javaeenotes.ws.ExampleWS",
wsdlLocation = "WEB-INF/wsdl/ExampleWSService.wsdl")
//@SchemaValidation
@HandlerChain(file = "handlers.xml")
public class WsImplementation implements ExampleWS {

@Override
public int sum(int arg0, int arg1) {
return 0;
}

@Override
public String greet(String arg0) {
return null;
}

@Override
public int multiply(int arg0, int arg1) {
return 0;
}
}

Tuesday, February 8, 2011

Building a contract-first webservice with JAX-WS

In this tutorial, we'll be building a webservice contract-first. This means we build the webservice based on a contract that already exists. This contract is the WSDL file that describes the webservice formally.

The first step is to generate JAX-WS classes using wsgen that comes with the Java EE SDK. You can find the executable in your SDK/Glassfish installation directory. We'll be using the WSDL file of an earlier blogpost. Save this file as: ExampleWSService.wsdl. If you're using Eclipse, you might want to save the files in WebContent/WEB-INF/wsdl in the project directory.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://javaeenotes.com/"
name="ExampleWSService"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://javaeenotes.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types>
<xsd:schema>
<xsd:import namespace="http://javaeenotes.com/" schemaLocation="ExampleWSService.xsd"/>
</xsd:schema>
</types>
<message name="sum">
<part name="parameters" element="tns:sum"/>
</message>
<message name="sumResponse">
<part name="parameters" element="tns:sumResponse"/>
</message>
<message name="greet">
<part name="parameters" element="tns:greet"/>
</message>
<message name="greetResponse">
<part name="parameters" element="tns:greetResponse"/>
</message>
<message name="multiply">
<part name="parameters" element="tns:multiply"/>
</message>
<message name="multiplyResponse">
<part name="parameters" element="tns:multiplyResponse"/>
</message>
<portType name="ExampleWS">
<operation name="sum">
<input message="tns:sum"/>
<output message="tns:sumResponse"/>
</operation>
<operation name="greet">
<input message="tns:greet"/>
<output message="tns:greetResponse"/>
</operation>
<operation name="multiply">
<input message="tns:multiply"/>
<output message="tns:multiplyResponse"/>
</operation>
</portType>
<binding name="ExampleWSPortBinding" type="tns:ExampleWS">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sum">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="greet">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="multiply">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="ExampleWSService">
<port name="ExampleWSPort" binding="tns:ExampleWSPortBinding">
<soap:address location="http://127.0.0.1:8080/webservice/ExampleWSService"/>
</port>
</service>
</definitions>


This WSDL file imports an XSD file. Make sure this is placed in the same directory as the WSDL file, and name it ExampleWSService.xsd. Also update the location/URL in the WSDL file. This example assumes the application is called webservice and the service name is ExampleWSService.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0"
targetNamespace="http://javaeenotes.com/"
xmlns:tns="http://javaeenotes.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="greet" type="tns:greet"/>

<xs:element name="greetResponse" type="tns:greetResponse"/>

<xs:element name="multiply" type="tns:multiply"/>

<xs:element name="multiplyResponse" type="tns:multiplyResponse"/>

<xs:element name="sum" type="tns:sum"/>

<xs:element name="sumResponse" type="tns:sumResponse"/>

<xs:complexType name="greet">
<xs:sequence>
<xs:element name="arg0" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="greetResponse">
<xs:sequence>
<xs:element name="return" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="sum">
<xs:sequence>
<xs:element name="arg0" type="xs:int"/>
<xs:element name="arg1" type="xs:int"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="sumResponse">
<xs:sequence>
<xs:element name="return" type="xs:int"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="multiply">
<xs:sequence>
<xs:element name="arg0" type="xs:int"/>
<xs:element name="arg1" type="xs:int"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="multiplyResponse">
<xs:sequence>
<xs:element name="return" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>


Now use the wsgen command to generate the server stubs. I generally put the complete command in a Windows batch file, so I can easily rerun the command in the future if necessary. Now, execute the following command:

wsimport -p com.javaeenotes.ws -d build/classes -s src WebContent/WEB-INF/wsdl/ExampleWSService.wsdl


This will generate all classes needed to setup the webservice. Next is to create a class that implements the webservice.


package com.javaeenotes;

import javax.jws.WebService;

import com.javaeenotes.ws.ExampleWS;

@WebService(name = "ERSService",
targetNamespace = "http://javaeenotes.com/",
serviceName = "ExampleWSService",
portName = "ExampleWSPort",
endpointInterface = "com.javaeenotes.ws.ExampleWS",
wsdlLocation = "WEB-INF/wsdl/ExampleWSService.wsdl")
public class WsImplementation implements ExampleWS {

@Override
public int sum(int arg0, int arg1) {
return 0;
}

@Override
public String greet(String arg0) {
return null;
}

@Override
public int multiply(int arg0, int arg1) {
return 0;
}
}


This class implements the webservice interface we generated from the WSDL file. Using simple annotations, we can easily deploy it without editting any deployment descriptors. Nothing more is needed for successful deployment. If you used the default URL in the WSDL file, you can now reach the webservice using this URL:

http://127.0.0.1:8080/webservice/ExampleWSService?wsdl

I recommend SOAP-UI to test the webservice.

Saturday, February 5, 2011

RESTful webservices using JAX-RS

This tutorial demonstrates how RESTful services are created using JAX-RS. We'll be using Glassfish as our primary application server. This tutorial is limited to reading representational objects. For implementation information about CRUD-operations and REST, I recommend the following online book.

The tutorial consists of the following steps:

1. Define URLs
2. Define data format
3. Configure JAX-RS in web.xml
4. Create REST POJO's
5. Test application with URLs


1. Define URLs

The first step is define the URLs we'll be using in accessing the REST objects. For this example, we define four relative URLs:

1. All departments: /departments/
2. Department by ID: /departments/{id}
3. All employees: /employees/
4. Employee by ID: /employees/{id}

The {id} is the ID parameter, which should be an integer number.


2. Define data format

In this step we have to choose how we want to format the object for the client. Commonly, this will be either XML or JSON. The latter one is definitely preferred if AJAX is handling the output. But for this tutorial, we'll be using XML.


<department id="12345">
<link rel="self" href="/departments/12345" />
<name>Solutions Development</name>
</department>



<employee id="1580">
<link rel="self" href="/employees/1580" />
<firstName>Gin Lung</firstName>
<lastName>Cheng</lastName>
<department id="12345">
<link rel="self" href="/departments/12345" />
</department>
</employee>



3. Configure JAX-RS in web.xml

Now, we need to configure the application server to support RESTful services by putting the following XML-code in the web.xml file inside the <web-app>-element:


<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/rs/*</url-pattern>
</servlet-mapping>


This tells our application server that we want URLs that start with /rs/ are handled by the JAX-RS Servlet.


4. Create REST POJO's

Create the POJO's that contain the methods behind the URLs. For demonstration purposes, the output is hardcoded. Normally, content is retrieved from a database, and converted to XML with JAX-B.

DepartmentService.java

package com.javaeenotes;

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

@Path("/departments")
public class DepartmentService {

@GET
@Produces("application/xml")
public String showDepartments() {
// Return all departments.
return "<department id=\"12345\">"
+ "<link rel=\"self\" href=\"/departments/12345\" />"
+ "<name>Solutions Development</name>"
+ "</department>";
}

@Path("{id}")
@GET
@Produces("application/xml")
public String showDepartment(@PathParam("id") int id) {
// Return department by ID.
return "<department id=\"" + id + "\">"
+ "<link rel=\"self\" href=\"/departments/" + id + "\" />"
+ "<name>Solutions Development</name>"
+ "</department>";
}
}



EmployeeService.java

package com.javaeenotes;

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

@Path("/employees")
public class EmployeeService {

@GET
@Produces("application/xml")
public String showEmployees() {
// Return all employees.
return "<employee id=\"1580\">"
+ "<link rel=\"self\" href=\"/employees/1580\" />"
+ "<firstName>Gin Lung</firstName>"
+ "<lastName>Cheng</lastName>"
+ "<department id=\"12345\">"
+ "<link rel=\"self\" href=\"/departments/12345\" />"
+ "</department>"
+ "</employee>";
}

@Path("{id}")
@GET
@Produces("application/xml")
public String showEmployee(@PathParam("id") int id) {
// Return employee by ID.
return "<employee id=\"" + id + "\">"
+ "<link rel=\"self\" href=\"/employees/" + id + "\" />"
+ "<firstName>Gin Lung</firstName>"
+ "<lastName>Cheng</lastName>"
+ "<department id=\"12345\">"
+ "<link rel=\"self\" href=\"/departments/12345\" />"
+ "</department>"
+ "</employee>";
}
}



5. Test application with URLs
If you deployed your application on a default install of Glassfish on your computer, the objects can be retrieved using the following URLs:

http://localhost:8080/restfulservices/rs/departments
http://localhost:8080/restfulservices/rs/departments/12345
http://localhost:8080/restfulservices/rs/employees
http://localhost:8080/restfulservices/rs/employees/1580

If you use Firefox to do this, remember that Firefox will render XML-tags invisible. Try to view the source of the page.

Wednesday, February 2, 2011

Site for wallpapers

Not really Java related, but still worth mentioning on this blog. InterfaceLIFT is my favorite site for downloading amazing wallpapers.

Use their filter mechanism to find a wallpaper for your desktop/laptop. You can sort results based on downloads or rating. If you're a multi-screen user like I am, you can choose to show only results that are for 2 or even 3 screens. You can then download the wallpaper in various screen resolutions.