Freitag, 8. Juli 2011

Java 7 Spike

Oracle released the JDK developer preview for Java SE 7. This release is feature complete, stable and ready for testing. These were enough reasons to write a short Java SE 7 Spike. The first step writing the Spike was to install the JDK. After the installation of the JDK, I installed Eclipse Indigo for Java Developers and a patch to enable the Java 7 compiler compliance level in Eclipse. The Spike involves small tests to gain experience and knowledge about Java 7 language enhancements which are hosted in the Coin Project.

The Spike covers the following Java 7 features:

  • strings in switch statements
  • underscores in numeric literals
  • catching multiple exceptions
  • rethrow exceptions with improved type checking
  • type inference for generic instance creation (Diamond)
  • try-with-resources statement

Java 7 Spike:

import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
import org.junit.Test;

public class Java7Spike {

    @Test
    public void testStringsInSwitch() {
       
        assertEquals("right direction", decideDirection("right"));
        assertEquals("left direction", decideDirection("left"));
        assertEquals("no direction", decideDirection("no"));
    }

    private String decideDirection(final String decision) {
       
        switch(decision) {
       
            case "right":
               
                return "right direction";
           
            case "left":
               
                return "left direction";
           
            default:
               
                return "no direction";   
        }
    }
   
    @Test
    public void testUnderscoreInNumericLiterals() {
       
        int numericLiteral = 12_30;
        numericLiteral++;
       
        assertEquals(1231, numericLiteral);
    }
   
    @Test
    public void testMultiCatch() {
       
        try {
           
            final String fileText = readFile("src/org/ccd/java/features/testNio.txt");
            parseFile(fileText);           
        }
        catch (IOException | ParseException ex) {
           
            throw new IllegalStateException("Error during file handling occured!", ex);
        } 
    }
   
    private String readFile(final String filename) throws IOException {return null;}
    private void parseFile(final String content) throws ParseException {}
   
    @Test(expected=FirstException.class)
    public void testRethrowExceptionWithTypeChecking()
                                          throws FirstException, SecondException {       
        try {
           
            simulateFailure();
        }
        catch (Exception ex) {
         
            throw ex;
        }         
    }

    private void simulateFailure() throws FirstException {
       
        throw new FirstException();
    }

    private class FirstException extends Exception {
       
        private static final long serialVersionUID = -1872200400984813924L;
    }
   
    private class SecondException extends Exception {
       
        private static final long serialVersionUID = 7453507163657978623L;
    }
   
    @Test
    public void testDiamond() {
   
        final List<String> list = new LinkedList<>();
        list.add("anEntry");
       
        assertEquals(1, list.size());
    }
   
    @Test
    public void testTryWithResources() throws IOException {
       
          final Path file = Paths.get("src/org/ccd/java/features/testNio.txt");
         
          try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(file)) {

              final ByteBuffer buffer = ByteBuffer.allocate(100_000);

              channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {

                  public void completed(Integer result, ByteBuffer attachment) {
                 
                      assertEquals(19, result.intValue());
                  }
   
                  public void failed(Throwable exception, ByteBuffer attachment) {
                 
                      exception.printStackTrace();
                  }               
              });
         }
    }
}

I am looking forward to Java SE 8 which may include further language enhancements, summarized in the Coin Project and additional features like the integration of Closures (JSR 335 / Lambda Project).


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.

Montag, 4. Juli 2011

Catch the web service bug

Agile developer catch bugs with tests. Unit tests improve the internal quality of a software system and acceptance tests the external quality visible to the users. The testing effort reduces the defect rate and increases maintainability. Smart testing is a shield against the trap of slow and high cost development that could occur by poor written code which is not covered by tests. The fast feedback of the tests gives developers confidence and more time for other tasks in the software development cycle.

But take care! Test driven development (TDD) including high test coverage is not the guarantee to pass all possible problems. There is the danger to walk in a bad direction forgetting the big picture during refactoring. This is also true working in the flow zone, forgetting the picture which the software model defines. Some refactoring steps following KISS and pragmatic programming could possibly drive the ball also in a new direction, which could be in the future a problem while extending the software due to a change request.

Agile developers are careful and aware of such traps. TDD should start at the major components. The model of a software solution is a typical candidate for TDD. This is also true for web services which are simply backend services with a remote interface accessible via the internet. This sounds simple, but is it simple? There is a network between the backend service and the user of the web service. Networks may be slow, sometimes the connection is missing and the world wide web is like the deep universe. A good reason to limit the access to web services and secure them as strong as possible. Securing web services and fulfill all the non-functional requirements needs time and can’t be covered with a simple unit test. Special testing is necessary beneath the unit and acceptance tests. Agile developers know that they have to have a test pyramid in the pocket!

The lowest levels of the pyramid are unit and acceptance tests. From the developer perspective the aim to catch bugs should be to find them as early as possible. Perfectly with unit tests not using additional acceptance test frameworks. Java SE 6 gives web service developers an endpoint publisher that offers the possibility to test end to end in simple unit tests. This statement fits for SOAP- and REST-based web services which are based on JAX-WS. 

Here comes a little story about the competition of SOAP- and REST-style web services:

The opinion that REST-style web services are the vehicle which solves a lot of problems easily and pretty lightweight flows around in the community. I’ve heard that often. Few days ago, I read a statement in a Java EE blog and wrote a few lines:

JAX-WS is a mature technology for building SOAP- and REST-based web services. The REST part of JAX-WS is not comfortable enough for web service developer, therefore JAX-RS was borne which is easier to use even with more functionality. But this is not the point! 

The point is that JAX-WS (SOAP part) and JAX-RS are both legal depending on use cases. The real benefit of SOAP-based web services is the WSDL which defines the web service interface. The top-down approach (WSDL2Java) is still a best practice to solve interoperability problems. WSDL interfaces are clear and (quasi) normed by using the WS-I basic profile as a guide for web service development. 

The WSDL is the important artifact from which the generation of Java artifacts is easy by tooling. The power of WSDL is important in Enterprise Service Bus (ESB) environments and Java Business Integration (JBI) components, which are described by WSDL. My opinion is that REST is not the big player in ESB environments. I think you need a definition (for SOAP services the WSDL) to handle parameter types and encodings, protocol transformations, routings and mediation. Therefore, I see REST rather as the lightweight and maybe agile gate to portal backend systems to build single processes (no orchestration necessary).

I fully agree that JAX-RS makes coding funny and I like to write RESTful web services rather to write a complex WSDL file (although Eclipse support is available). The lightweight approach of REST is nice, but has a danger. The input line of the browser (you need no scripting to call the service) is the direct gateway to the database for a not properly secured REST service. To secure the REST service is still effort, including configuration and programming tasks.


The response came very fast: Which use cases?!

The simplest use case is the usage of an existing session facade. The publication of the facade as web service is easy by adding an annotation (@WebService). More fine granular by using annotations on the facade methods (@WebMethod). This use case is not really a strong point, but doing the same with REST is more coding and design effort for the simple goal to use the legacy software as web service.

A stronger point is that with JAX-WS the developer defines domain specific interfaces (domain specific verbs) which are self-describing by the WSDL. The REST interface instead is generic (set of defined verbs) leaving the domain specific subjects in the addressed resources. This is essential! JAX-WS (SOAP-part) is therefore primarily activity-oriented and REST is strongly resource-oriented. The architecture of REST is based on the generic interface for mainly CRUD operations. You see it also in the Response and Fault codes. SOAP faults are domain specific. REST faults are Http specific error codes. To use the response for the domain specific fault message in association with Http state 200 (OK) is a poor decision. This leads to a possible use case were domain specific faults has to be traced and processed (JAX-WS - SOAP fault has a benefit!).


Developer can build complex interactions with REST. Even multipart messages are possible with JAX-RS. The argument that REST is inherently stateless and SOAP has the possibility to define stateful services can be moderated by defining a stateless architecture not holding any state on the server side. On the other side, I see some bricks to lay out use cases.


When we come to ESBs the strong points occur. SOAP services can be bound to different protocols. ESBs work internally with JMS to cover the requirements of the pipeline architecture. Without the possibility to bound SOAP to different protocols this architecture which is a key point for SOA wouldn’t as it is today. The SOAP extensibility model is the key, defining SOAP nodes processing the header of the SOAP envelope for routing and mediation issues. We do not have such functions for REST. REST is easy but tightly coupled with the Http protocol. I do not actually see the orchestration and workflow requirements covered by REST where human tasks are involved and the reply is minutes, sometimes hours or days away from the primary request.

I told you that short story that you have a feeling for
the effort of writing the SOA- and REST-parts which are published in the following unit test with the endpoint publisher. You see in the unit tests how easy it is to publish a SOAP-based web service from an existing model and the additional effort to use the existing model in a REST-based web service. I admit that the RESTful web service is a Java EE 5 one. Java EE 6 and JAX-RS makes the implementation of RESTful web services simpler.

The backend model of the web service is a quiz application with two simple operations. I show only the unit tests of the web service backend model and the web service test parts. The implementation of the backend service is not interesting in this context. My goal is to show the endpoint publisher in action and not the implementation of simplified web services.

Web Service backend model test:

@RunWith(value=Parameterized.class)
public class QuizServiceTest {

    private String[][] quizData;
    private final String question;
    private final String answer;
   
    private Quiz quizService;
   
    public QuizServiceTest(final String[] questions,
                                      final String[] answers) {
       
        this.question = questions[0];
        this.answer = answers[0];
       
        initializeQuizData(questions, answers);
    }

    private void initializeQuizData(final String[] questions,
                                               final String[] answers) {
       
        quizData = new String[1][2];
        quizData[0][0] = questions[0];
        quizData[0][1] = answers[0];
    }
   
    @Parameters
    public static Collection<String[][]> getTestParameters() {
      
        return Arrays.asList(new String[][][] {
              
                {{"firstQuestion"},{"firstAnswer"}},
                {{"secondQuestion"},{"secondAnswer"}}});
    }
   
    @Before
    public void setUp() {
       
        quizService = new QuizServiceController();
        quizService.initialize(quizData);       
    }
   
    @Test
    public void testQuizCorrectAnswer() {

        assertEquals(true, quizService.answer(question, answer));
    }
   
    @Test
    public void testQuizWrongAnswer() {
           
        assertEquals(false, quizService.answer(question, "anAnswer"));
    }
}

SOAP-Part unit test using the endpoint publisher:

@RunWith(value = Parameterized.class)
public class QuizSOAPServiceEndToEndTest {

    private static final String PUBLISH_URL = "http://127.0.0.1:8080/quiz";
    private static final String WSDL_URL = "http://127.0.0.1:8080/quiz?wsdl";
    private static final String QNAME_NAMESPACE_URL = "http://quiz.service.web.java.ccd.org/";
    private static final String QNAME_SERVICE_NAME = "QuizServiceService";
   
    private String[][] quizData;
    private final String question;
    private final String answer;
   
    private QuizServiceSOAP quizService;

    public QuizSOAPServiceEndToEndTest(final String[] questions,
                                                              final String[] answers) {

        this.question = questions[0];
        this.answer = answers[0];

        initializeQuizData(questions, answers);
    }

    private void initializeQuizData(final String[] questions,
                                               final String[] answers) {
       
        quizData = new String[1][2];
        quizData[0][0] = questions[0];
        quizData[0][1] = answers[0];
    }

    @Parameters
    public static Collection<String[][]> getTestParameters() {

        return Arrays.asList(new String[][][] {

                {{"firstQuestion"},{"firstAnswer"}},
                {{"secondQuestion"},{"secondAnswer"}}});
    }

    @Before
    public void setUp() throws MalformedURLException {

        publishQuizService();       
        lookupQuizService();       
        initializeQuizService();
    }

    private void publishQuizService() {
       
        Endpoint.publish(PUBLISH_URL, new QuizService());
    }

    private void lookupQuizService() throws MalformedURLException {
       
        final URL url = new URL(WSDL_URL);
        final QName qname = new QName(QNAME_NAMESPACE_URL,QNAME_SERVICE_NAME);
        final Service service = Service.create(url,qname);
        quizService = service.getPort(QuizServiceSOAP.class);   
    }

    private void initializeQuizService() {
       
        quizService.initialize(quizData);
    }

    @Test
    public void testQuizCorrectAnswer() {

        assertEquals(true, quizService.answer(question, answer));
    }

    @Test
    public void testQuizWrongAnswer() {

        assertEquals(false, quizService.answer(question, "anAnswer"));
    }
}

The publishing of the SOAP-based web service in the unit test is simple and fetching the service after publishing it is possible in a leightweight manner. Publishing the web service first and using later the browser or an external web service test tool based on the retrieval of the WSDL is also an opportunity.

REST-part unit test using the endpoint publisher:

@RunWith(value = Parameterized.class)public class QuizRESTServiceEndToEndTest {
   
    private static final String XPATH_QUIZ_URI_PART = "quiz";
    private static final String XPATH_QUIZ_RESPONSE = "/quiz:response";
    private static final String GET_REQUEST = "GET";
    private static final String POST_REQUEST = "POST";

    private static final String QUIZ_PORT = "quizPort";
    private static final String QUIZ_SERVICE = "quizService";
    private static final String QUIZ_URI = "urn:quiz";

    private static final String PUBLISH_URL = "http://127.0.0.1:8080/quiz";
   
    private final String XML_START = "<quiz:request xmlns:quiz ='urn:quiz'>";
    private final String XML_END = "</quiz:request>";

    private static final String FALSE_RESPONSE = "false";
    private static final String TRUE_RESPONSE = "true";
    private static final String QUESTION_DELIMITER = ":";
    private static final String QUESTION_ANSWER_DELIMITER = ";";
   
    private String[][] quizData;
    private final String question;
    private final String answer;
   
    private Dispatch<Source> dispatch;
    private String uri;

    public QuizRESTServiceEndToEndTest(final String[] questions,
                                                             final String[] answers) {

        this.question = questions[0];
        this.answer = answers[0];

        initializeQuizData(questions, answers);
    }

    private void initializeQuizData(final String[] questions,
                                               final String[] answers) {
       
        quizData = new String[1][2];
        quizData[0][0] = questions[0];
        quizData[0][1] = answers[0];
    }

    @Parameters
    public static Collection<String[][]> getTestParameters() {

        return Arrays.asList(new String[][][] {

                {{"firstQuestion"},{"firstAnswer"}},
                {{"secondQuestion"},{"secondAnswer"}}});
    }

    @Before
    public void setUp() throws URISyntaxException {

        publishQuizService();       
        lookupQuizService();   
   
        initializeQuizService();
    }

    private void publishQuizService() {
       
        Endpoint.publish(PUBLISH_URL, new QuizServiceRest());
    }

    private void lookupQuizService() throws URISyntaxException {
       
        uri = new URI(QUIZ_URI).toString();
           
        final QName serviceName = new QName(QUIZ_SERVICE, uri);
        final QName port = new QName(QUIZ_PORT, uri);
        final String endpoint = PUBLISH_URL;       
       
        final Service service = Service.create(serviceName);
        service.addPort(port,HTTPBinding.HTTP_BINDING, endpoint);
       
        dispatch = service.createDispatch(port,Source.class,Service.Mode.PAYLOAD);               
    }
   
    private void initializeQuizService() {
       
        invokePost(dispatch, buildPayload(convertQuizDataToString(quizData)));
    }
   
    private String invokePost(final Dispatch<Source> dispatch,                       
                                         final String payload) {
       
        final Map<String, Object> requestContext = dispatch.getRequestContext();
        requestContext.put(MessageContext.HTTP_REQUEST_METHOD, POST_REQUEST);       
       
        final StreamSource source = makeStreamSource(payload.toString());
        final Source result = dispatch.invoke(source);
       
        return parseResponse(result);
    }
   
    private StreamSource makeStreamSource(final String payload) {
       
        final ByteArrayInputStream stream = new ByteArrayInputStream(payload.getBytes());
        return new StreamSource(stream);
    }
       
    private String buildPayload(final String payload) {
       
        return XML_START + payload + XML_END;
    }
   
    private String convertQuizDataToString(final String[][] payload) {
       
        final StringBuilder builder = new StringBuilder();
       
        for(int i = 0; i < payload.length; i++) {
           
            builder.append(payload[i][0]);
            builder.append(QUESTION_DELIMITER);
            builder.append(payload[i][1]);
            builder.append(QUESTION_ANSWER_DELIMITER);
        }
       
        return builder.toString();
    }
   
    private String parseResponse(final Source response) {
                                   
        final DOMResult domResult = new DOMResult();
        String result = null;
        try {
           
            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.transform(response, domResult);
           
            final XPathFactory xpFactory = XPathFactory.newInstance();
            final XPath xp = xpFactory.newXPath();
           
            xp.setNamespaceContext(new NSResolver(XPATH_QUIZ_URI_PART, QUIZ_URI));
           
            result = xp.evaluate(XPATH_QUIZ_RESPONSE, domResult.getNode());                               
        }
        catch (TransformerConfigurationException ex) {
           
            throw new IllegalStateException(ex);
        }
        catch (TransformerFactoryConfigurationError ex) {
           
            throw new IllegalStateException(ex);
        }
        catch (TransformerException ex) {
           
            throw new IllegalStateException(ex);
        }
        catch (XPathExpressionException ex) {
           
            throw new IllegalStateException(ex);
        }
       
        return result;
    }
   
    @Test
    public void testQuizCorrectAnswer() {

        assertEquals(TRUE_RESPONSE,
                            invokeGet(dispatch,question + QUESTION_DELIMITER + answer));
    }

    @Test
    public void testQuizWrongAnswer() {

        assertEquals(FALSE_RESPONSE,
                           invokeGet(dispatch,question + QUESTION_DELIMITER + "anAnswer"));
    }
   
    private String invokeGet(final Dispatch<Source> dispatch,                           
                                       final String queryString) {
       
        final Map<String, Object> requestContext = dispatch.getRequestContext();
        requestContext.put(MessageContext.HTTP_REQUEST_METHOD, GET_REQUEST);
        requestContext.put(MessageContext.QUERY_STRING, queryString);
       
        final Source result = dispatch.invoke(null);
       
        return parseResponse(result);
    }
}

The RESTful web service unit test uses the endpoint publisher and the dispatch interface. The server side RESTful web services implements the provider interface. It is obviously more coding effort in the given scenario to publish the quiz backend model as RESTful web service in the unit test and the service implementation. The SOAP-part is in line with the quiz backend model, simply using the JAX-WS annotations. For the RESTful part it's (slightly) a little bit to much coding necessary. Praise for Java EE 6 which gives us JAX-RS, to reduce the coding effort of RESTful web services noticeable.


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.