Java tutorial
package com.azaptree.services.command.http.handler; /* * #%L * AZAPTREE-COMMAND-HTTP-SERVICE * %% * Copyright (C) 2012 - 2013 AZAPTREE.COM * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.eclipse.jetty.client.ContentExchange; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.ByteArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; import org.testng.Assert; import org.testng.annotations.Test; import com.azaptree.services.command.CommandCatalog; import com.azaptree.services.command.CommandKey; import com.azaptree.services.command.CommandService; import com.azaptree.services.command.http.WebCommandContext; import com.azaptree.services.command.http.WebXmlRequestCommand; import com.azaptree.services.command.impl.CommandCatalogImpl; import com.azaptree.services.command.impl.CommandServiceImpl; import com.azaptree.services.command.messages.AdditionRequestMessage; import com.azaptree.services.command.messages.AdditionResponseMessage; import com.azaptree.services.command.messages.HeartbeatMessage; import com.azaptree.services.command.messages.ObjectFactory; import com.azaptree.services.http.HttpService; import com.azaptree.services.http.HttpServiceConfig; import com.azaptree.services.http.headers.ResponseMessageHeaders; import com.azaptree.services.http.impl.ExecutorThreadPoolWithGracefulShutdown; import com.azaptree.services.http.impl.HttpServiceImpl; import com.google.common.base.Optional; @ContextConfiguration(classes = { CommandServiceHandlerTest.Config.class }) public class CommandServiceHandlerTest extends AbstractTestNGSpringContextTests { @Configuration public static class Config { @Bean WebXmlRequestCommand<AdditionRequestMessage, AdditionResponseMessage> addNumbersCommand() { return new WebXmlRequestCommand<AdditionRequestMessage, AdditionResponseMessage>( AdditionRequestMessage.class, AdditionResponseMessage.class) { @Override protected boolean executeCommand( final WebCommandContext<AdditionRequestMessage, AdditionResponseMessage> ctx) { final HttpServletResponse response = ctx.getHttpServletResponse(); response.setStatus(HttpStatus.OK_200); response.setCharacterEncoding("UTF-8"); response.setContentType("application/xml"); final AdditionRequestMessage requestMessage = ctx.getRequestMessage(); double sum = 0; for (final double number : requestMessage.getNumber()) { sum += number; } final AdditionResponseMessage responseMessage = new AdditionResponseMessage(); responseMessage.setSum(sum); ctx.setResponseMessage(responseMessage); final ObjectFactory objectFactory = new ObjectFactory(); writeResponseMessage(ctx.getHttpServletResponse(), objectFactory.createAddNumbersResponse(responseMessage)); return true; } @Override public Optional<QName> getRequestXmlElement() { final ObjectFactory objectFactory = new ObjectFactory(); final QName elemName = objectFactory .createAddNumbersRequest(objectFactory.createAdditionRequestMessage()).getName(); return Optional.of(elemName); } @Override public Optional<QName> getResponseXmlElement() { final ObjectFactory objectFactory = new ObjectFactory(); final QName elemName = objectFactory .createAddNumbersResponse(objectFactory.createAdditionResponseMessage()).getName(); return Optional.of(elemName); } }; } @Bean CommandCatalog commandCatalog() { return new CommandCatalogImpl("CommandServiceHandlerTest", helloWorldCommand(), addNumbersCommand(), errorCommand(), new WebXmlRequestCommand<HeartbeatMessage, HeartbeatMessage>("heartbeat", HeartbeatMessage.class, HeartbeatMessage.class) { @Override protected boolean executeCommand( final WebCommandContext<HeartbeatMessage, HeartbeatMessage> ctx) { ctx.setResponseMessage(ctx.getRequestMessage()); writeResponseMessage(ctx); return false; } @Override public Optional<QName> getRequestXmlElement() { return Optional.of(new QName("http://www.azaptree.com/test", "heartbeat-message")); } @Override public Optional<QName> getResponseXmlElement() { return Optional.of(new QName("http://www.azaptree.com/test", "heartbeat-message")); } }); } @Bean CommandService commandService() { return new CommandServiceImpl(); } @Bean WebXmlRequestCommandServiceHandler commandServiceHandler() { return new WebXmlRequestCommandServiceHandler(executor(), "http://localhost:8080"); } @SuppressWarnings("rawtypes") @Bean WebXmlRequestCommand errorCommand() { return new WebXmlRequestCommand() { @Override protected boolean executeCommand(final WebCommandContext ctx) { throw new RuntimeException("ERROR"); } @Override public Optional getRequestXmlElement() { return Optional.absent(); } @Override public Optional getResponseXmlElement() { return Optional.absent(); } }; } @Bean(destroyMethod = "shutdown") Executor executor() { return Executors.newCachedThreadPool(); } @SuppressWarnings("rawtypes") @Bean WebXmlRequestCommand helloWorldCommand() { return new WebXmlRequestCommand() { @Override protected boolean executeCommand(final WebCommandContext ctx) { final HttpServletResponse response = ctx.getHttpServletResponse(); response.setStatus(HttpStatus.OK_200); response.setContentType(MimeTypes.TEXT_PLAIN_UTF_8); try (final PrintWriter pw = response.getWriter()) { pw.print("TIMESTAMP :"); pw.print(DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(System.currentTimeMillis())); } catch (final IOException e) { throw new IllegalStateException("Failed to obtain HTTP response writer", e); } return true; } @Override public Optional getRequestXmlElement() { return Optional.absent(); } @Override public Optional getResponseXmlElement() { return Optional.absent(); } }; } @Bean(destroyMethod = "stop") HttpClient httpClient() throws Exception { final HttpClient client = new HttpClient(); client.setThreadPool(new ExecutorThreadPoolWithGracefulShutdown(Executors.newCachedThreadPool(), 30)); client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); client.start(); return client; } @Bean HttpService httpService() { return new HttpServiceImpl(httpServiceConfig()); } @Bean HttpServiceConfig httpServiceConfig() { return new HttpServiceConfig("command-service", commandServiceHandler()); } } public static String prettyFormat(final String input, final int indent) { try { final Source xmlInput = new StreamSource(new StringReader(input)); final StringWriter stringWriter = new StringWriter(); final StreamResult xmlOutput = new StreamResult(stringWriter); final TransformerFactory transformerFactory = TransformerFactory.newInstance(); transformerFactory.setAttribute("indent-number", indent); final Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString(); } catch (final Exception e) { throw new RuntimeException(e); // simple exception handling, please review it } } private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired private HttpServiceConfig httpSericeConfig; @Autowired private HttpClient client; @Resource(name = "addNumbersCommand") private WebXmlRequestCommand<AdditionRequestMessage, AdditionResponseMessage> addNumbersCommand; @Autowired private CommandService commandService; @Test public void test_addNumbersCommand() throws IOException, JAXBException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("POST"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "addNumbersCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); contentExchange.setRequestContentType("application/xml"); final AdditionRequestMessage requestMessage = new AdditionRequestMessage(); requestMessage.getNumber().add(1d); requestMessage.getNumber().add(2d); requestMessage.getNumber().add(3d); final Marshaller marshaller = addNumbersCommand.getJaxbContext().get().createMarshaller(); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final ObjectFactory objectFactory = new ObjectFactory(); marshaller.marshal(objectFactory.createAddNumbersRequest(requestMessage), bos); System.out.println(bos.toString()); contentExchange.setRequestContent(new ByteArrayBuffer(bos.toByteArray())); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.OK_200); final byte[] responseContent = contentExchange.getResponseContentBytes(); final ByteArrayInputStream bis = new ByteArrayInputStream(responseContent); log.info("test_addNumbersCommand(): response content: {}", new String(responseContent)); final Unmarshaller unmarshaller = addNumbersCommand.getJaxbContext().get().createUnmarshaller(); final JAXBElement<AdditionResponseMessage> responseMessage = (JAXBElement<AdditionResponseMessage>) unmarshaller .unmarshal(bis); Assert.assertEquals(responseMessage.getValue().getSum(), 1d + 2d + 3d); } @Test public void test_errorCommad() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("POST"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "errorCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.INTERNAL_SERVER_ERROR_500); Assert.assertTrue(StringUtils.isNotBlank( contentExchange.getResponseFields().getStringField(ResponseMessageHeaders.STATUS_MSG.header))); } @Test public void test_getCommandWithNoXSD() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("GET"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "helloWorldCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s.xsd", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.NO_CONTENT_204); final byte[] responseContent = contentExchange.getResponseContentBytes(); Assert.assertTrue(ArrayUtils.isEmpty(responseContent)); } @Test public void test_getCommandXSD() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("GET"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "addNumbersCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s.xsd", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.OK_200); final byte[] responseContent = contentExchange.getResponseContentBytes(); final String xsd = new String(responseContent, "UTF-8"); Assert.assertTrue(StringUtils.isNotBlank(xsd)); log.info("test_getCommandXSD() xsd :\n{}", prettyFormat(xsd, 4)); } @Test public void test_getWADL() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("GET"); contentExchange .setURL(String.format("http://localhost:%d/command-service.wadl", httpSericeConfig.getPort())); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.OK_200); final byte[] responseContent = contentExchange.getResponseContentBytes(); final String xsd = new String(responseContent, "UTF-8"); Assert.assertTrue(StringUtils.isNotBlank(xsd)); log.info("test_getWADL() xsd :\n{}", prettyFormat(xsd, 4)); } @Test public void test_hearbeat() throws IOException, InterruptedException, JAXBException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("POST"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "heartbeat"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); final WebXmlRequestCommand<String, String> cmd = (WebXmlRequestCommand<String, String>) commandService .getCommand(new CommandKey(commandCatalogName, commandName)); final JAXBContext jaxbCtx = cmd.getJaxbContext().get(); final Marshaller marshaller = jaxbCtx.createMarshaller(); final StringWriter sw = new StringWriter(); final HeartbeatMessage hearbeat = new HeartbeatMessage(); hearbeat.setMessage("test_hearbeat()"); marshaller.marshal(hearbeat, sw); contentExchange.setRequestContent(new ByteArrayBuffer(sw.toString())); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.OK_200); final Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); final HeartbeatMessage responseMsg = (HeartbeatMessage) unmarshaller .unmarshal(new ByteArrayInputStream(contentExchange.getResponseContentBytes())); Assert.assertEquals(responseMsg.getMessage(), hearbeat.getMessage()); } @Test public void test_helloWorldCommand() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("POST"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "helloWorldCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.OK_200); final String responseMsg = contentExchange.getResponseContent(); log.info("test_helloWorldCommand() : responseMsg : {}", responseMsg); Assert.assertTrue(StringUtils.startsWith(responseMsg, "TIMESTAMP :")); } @Test public void test_invalidRequestXmlMessage() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("POST"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "addNumbersCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); contentExchange.setRequestContentType("application/xml"); contentExchange.setRequestContent(new ByteArrayBuffer("<invalid-request />")); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.BAD_REQUEST_400); Assert.assertTrue(StringUtils.isNotBlank( contentExchange.getResponseFields().getStringField(ResponseMessageHeaders.STATUS_MSG.header))); } @Test public void test_unknownCommand_GET() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("GET"); final String commandCatalogName = "ABC"; final String commandName = "XYZ"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.NOT_FOUND_404); } @Test public void test_unknownCommand_POST() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("POST"); final String commandCatalogName = "ABC"; final String commandName = "XYZ"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.NOT_FOUND_404); } @Test public void test_unsupportedHttpMethod() throws IOException, InterruptedException { final ContentExchange contentExchange = new ContentExchange(true); contentExchange.setMethod("PUT"); final String commandCatalogName = "CommandServiceHandlerTest"; final String commandName = "helloWorldCommand"; contentExchange.setURL(String.format("http://localhost:%d/command-service/%s/%s", httpSericeConfig.getPort(), commandCatalogName, commandName)); client.send(contentExchange); contentExchange.waitForDone(); Assert.assertEquals(contentExchange.getResponseStatus(), HttpStatus.METHOD_NOT_ALLOWED_405); Assert.assertTrue(contentExchange.getResponseFields().getStringField(HttpHeaders.ALLOW).contains("GET")); Assert.assertTrue(contentExchange.getResponseFields().getStringField(HttpHeaders.ALLOW).contains("POST")); } @Test public void test_WebRequestCommand_getJAXBContext() { final Optional<JAXBContext> ctx = addNumbersCommand.getJaxbContext(); Assert.assertTrue(ctx.isPresent()); Assert.assertTrue(addNumbersCommand.getJaxbContext().get() == ctx.get()); } }