Initial support for the Java 6 Http Server

This commit is contained in:
Arjen Poutsma
2007-12-20 16:07:26 +00:00
parent 7ea6847bc8
commit 00fa3620f5
10 changed files with 536 additions and 31 deletions

View File

@@ -69,6 +69,10 @@ public class HttpServletConnection extends AbstractReceiverConnection
statusCodeSet = true;
}
/*
* Errors
*/
public boolean hasError() throws IOException {
return false;
}

View File

@@ -44,6 +44,9 @@ public interface HttpTransportConstants extends TransportConstants {
/** The "404 Not Found" status code. */
int STATUS_NOT_FOUND = 404;
/** The "405 Method Not Allowed" status code. */
int STATUS_METHOD_NOT_ALLOWED = 405;
/** The "500 Server Error" status code. */
int STATUS_INTERNAL_SERVER_ERROR = 500;

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2007 the original author or authors.
*
* 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.
*/
package org.springframework.ws.transport.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.sun.net.httpserver.HttpExchange;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.transport.AbstractReceiverConnection;
import org.springframework.ws.transport.EndpointAwareWebServiceConnection;
import org.springframework.ws.transport.FaultAwareWebServiceConnection;
import org.springframework.ws.transport.WebServiceConnection;
/**
* Implementation of {@link WebServiceConnection} that is based on the Java 6 HttpServer {@link HttpExchange}.
*
* @author Arjen Poutsma
* @since 1.5.0
*/
public class HttpExchangeConnection extends AbstractReceiverConnection
implements EndpointAwareWebServiceConnection, FaultAwareWebServiceConnection {
private final HttpExchange httpExchange;
private ByteArrayOutputStream responseBuffer;
private int responseStatusCode = HttpTransportConstants.STATUS_ACCEPTED;
/** Constructs a new exchange connection with the given <code>HttpExchange</code>. */
protected HttpExchangeConnection(HttpExchange httpExchange) {
Assert.notNull(httpExchange, "'httpExchange' must not be null");
this.httpExchange = httpExchange;
}
/** Returns the <code>HttpExchange</code> for this connection. */
public HttpExchange getHttpExchange() {
return httpExchange;
}
public void endpointNotFound() {
responseStatusCode = HttpTransportConstants.STATUS_NOT_FOUND;
}
protected void onClose() throws IOException {
httpExchange.close();
}
/*
* Errors
*/
public boolean hasError() throws IOException {
return false;
}
public String getErrorMessage() throws IOException {
return null;
}
/*
* Receiving request
*/
protected Iterator getRequestHeaderNames() throws IOException {
return httpExchange.getRequestHeaders().keySet().iterator();
}
protected Iterator getRequestHeaders(String name) throws IOException {
List headers = httpExchange.getRequestHeaders().get(name);
return headers != null ? headers.iterator() : Collections.EMPTY_LIST.iterator();
}
protected InputStream getRequestInputStream() throws IOException {
return httpExchange.getRequestBody();
}
/*
* Sending response
*/
protected void addResponseHeader(String name, String value) throws IOException {
httpExchange.getResponseHeaders().add(name, value);
}
protected OutputStream getResponseOutputStream() throws IOException {
if (responseBuffer == null) {
responseBuffer = new ByteArrayOutputStream();
}
return responseBuffer;
}
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
byte[] buf = responseBuffer.toByteArray();
httpExchange.sendResponseHeaders(responseStatusCode, buf.length);
OutputStream responseBody = httpExchange.getResponseBody();
FileCopyUtils.copy(buf, responseBody);
}
/*
* Faults
*/
public boolean hasFault() throws IOException {
return responseStatusCode == HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR;
}
public void setFault(boolean fault) throws IOException {
if (fault) {
responseStatusCode = HttpTransportConstants.STATUS_INTERNAL_SERVER_ERROR;
}
else {
responseStatusCode = HttpTransportConstants.STATUS_OK;
}
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2007 the original author or authors.
*
* 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.
*/
package org.springframework.ws.transport.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.Lifecycle;
import org.springframework.util.Assert;
/**
* Factory bean for the HTTP server built into Java 6.
*
* @author Arjen Poutsma
* @see
* @since 1.5.0
*/
public class HttpServerFactoryBean implements ApplicationContextAware, InitializingBean, Lifecycle, FactoryBean {
private static final Log logger = LogFactory.getLog(HttpServerFactoryBean.class);
public static final int DEFAULT_PORT = 8080;
private InetAddress bindAddress;
private int port = DEFAULT_PORT;
private int backlog = -1;
private boolean autoStartup = true;
private boolean running = false;
private final Object lifecycleMonitor = new Object();
private HttpServer httpServer;
private Map handlerMap = new HashMap();
private ApplicationContext applicationContext;
/**
* Set whether to automatically start the receiver after initialization.
* <p/>
* Default is <code>true</code>; set this to <code>false</code> to allow for manual startup.
*/
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
/** Sets the port the server will bind to. */
public void setPort(int port) {
this.port = port;
}
/** Sets the server back log. */
public void setBacklog(int backlog) {
this.backlog = backlog;
}
public void setHandlers(Properties handlers) {
this.handlerMap.putAll(handlers);
}
public void setHandlerMap(Map handlerMap) {
this.handlerMap.putAll(handlerMap);
}
/**
* Sets the local internet address the server will bind to. By default, it will accept connections on any/all local
* addresses.
*
* @throws UnknownHostException when the given address is not known
* @see ServerSocket#ServerSocket(int,int,java.net.InetAddress)
*/
public void setBindAddress(String bindAddress) throws UnknownHostException {
this.bindAddress = InetAddress.getByName(bindAddress);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void afterPropertiesSet() throws IOException {
if (bindAddress == null) {
bindAddress = InetAddress.getLocalHost();
}
InetSocketAddress bindSocketAddress = new InetSocketAddress(bindAddress, port);
httpServer = HttpServer.create(bindSocketAddress, backlog);
registerContexts(this.handlerMap);
if (autoStartup) {
start();
}
}
protected void registerContexts(Map handlerMap) {
if (handlerMap.isEmpty()) {
logger.warn("Neither 'contextMap' nor 'contexts' set on HttpServerFactoryBean");
}
else {
Iterator it = handlerMap.keySet().iterator();
while (it.hasNext()) {
String path = (String) it.next();
Object handler = handlerMap.get(path);
// Prepend with slash if not already present.
if (!path.startsWith("/")) {
path = "/" + path;
}
registerHandler(path, handler);
}
}
}
protected void registerHandler(String path, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(path, "Path must not be null");
Assert.notNull(handler, "Handler object must not be null");
// Eagerly resolve handler if referencing singleton via name.
if (handler instanceof String) {
String handlerName = (String) handler;
if (applicationContext.isSingleton(handlerName)) {
handler = applicationContext.getBean(handlerName, HttpHandler.class);
}
}
if (handler == null || !(handler instanceof HttpHandler)) {
throw new IllegalStateException("Cannot resolve handler [" + handler + "] to HttpHandler instance");
}
httpServer.createContext(path, (HttpHandler) handler);
if (logger.isDebugEnabled()) {
logger.debug("Mapped path [" + path + "] onto handler [" + handler + "]");
}
}
public void start() {
if (logger.isInfoEnabled()) {
logger.info("Starting HttpServer [" + httpServer.getAddress() + "]");
}
synchronized (lifecycleMonitor) {
running = true;
lifecycleMonitor.notifyAll();
}
httpServer.start();
}
public void stop() {
synchronized (lifecycleMonitor) {
running = false;
lifecycleMonitor.notifyAll();
}
httpServer.stop(0);
}
public boolean isRunning() {
synchronized (lifecycleMonitor) {
return running;
}
}
public Object getObject() throws Exception {
return httpServer;
}
public Class getObjectType() {
return HttpServer.class;
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2007 the original author or authors.
*
* 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.
*/
package org.springframework.ws.transport.http;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.support.SimpleWebServiceMessageReceiverObjectSupport;
/**
* @author Arjen Poutsma
* @since 1.5.0
*/
public class WebServiceHttpHandler extends SimpleWebServiceMessageReceiverObjectSupport implements HttpHandler {
public void handle(HttpExchange httpExchange) throws IOException {
if ("POST".equals(httpExchange.getRequestMethod())) {
WebServiceConnection connection = new HttpExchangeConnection(httpExchange);
try {
handleConnection(connection);
}
catch (Exception ex) {
logger.error(ex);
}
}
else {
httpExchange.sendResponseHeaders(HttpTransportConstants.STATUS_METHOD_NOT_ALLOWED, -1);
httpExchange.close();
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2007 the original author or authors.
*
* 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.
*/
package org.springframework.ws.transport.http;
import java.io.IOException;
import com.sun.net.httpserver.HttpServer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Arjen Poutsma
* @since 1.5.0
*/
public class Driver {
public static void main(String[] args) throws IOException {
ApplicationContext context =
new ClassPathXmlApplicationContext("httpserver-applicationContext.xml", Driver.class);
HttpServer server = (HttpServer) context.getBean("httpServer");
System.in.read();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2007 the original author or authors.
*
* 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.
*/
package org.springframework.ws.transport.http;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.springframework.util.FileCopyUtils;
/**
* @author Arjen Poutsma
* @since 1.5.0
*/
public class DummyHandler implements HttpHandler {
public void handle(HttpExchange exchange) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
writer.write("Hello world!");
writer.flush();
byte[] buf = os.toByteArray();
exchange.sendResponseHeaders(HttpTransportConstants.STATUS_OK, buf.length);
FileCopyUtils.copy(buf, exchange.getResponseBody());
exchange.close();
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright ${YEAR} the original author or authors.
*
* 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.
*/
package org.springframework.ws.transport.http;
import junit.framework.TestCase;
public class HttpServerFactoryBeanTest extends TestCase {
private HttpServerFactoryBean bean;
protected void setUp() throws Exception {
bean = new HttpServerFactoryBean();
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="httpServer" class="org.springframework.ws.transport.http.HttpServerFactoryBean">
<property name="bindAddress" value="127.0.0.1"/>
<property name="handlers">
<props>
<prop key="/">handler</prop>
</props>
</property>
</bean>
<bean id="handler" class="org.springframework.ws.transport.http.DummyHandler"/>
</beans>

View File

@@ -37,18 +37,14 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
private final Object lifecycleMonitor = new Object();
/**
* Return whether this server is currently active, that is, whether it has been set up but not shut down yet.
*/
/** Return whether this server is currently active, that is, whether it has been set up but not shut down yet. */
public final boolean isActive() {
synchronized (lifecycleMonitor) {
return active;
}
}
/**
* Return whether this server is currently running, that is, whether it has been started and not stopped yet.
*/
/** Return whether this server is currently running, that is, whether it has been started and not stopped yet. */
public final boolean isRunning() {
synchronized (lifecycleMonitor) {
return running;
@@ -56,7 +52,7 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
}
/**
* Set whether to automatically start the listener after initialization.
* Set whether to automatically start the receiver after initialization.
* <p/>
* Default is <code>true</code>; set this to <code>false</code> to allow for manual startup.
*/
@@ -64,16 +60,12 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
this.autoStartup = autoStartup;
}
/**
* Calls {@link #activate()} when the BeanFactory initializes the receiver instance.
*/
/** Calls {@link #activate()} when the BeanFactory initializes the receiver instance. */
public void afterPropertiesSet() throws Exception {
activate();
}
/**
* Calls {@link #shutdown()} when the BeanFactory destroys the receiver instance.
*/
/** Calls {@link #shutdown()} when the BeanFactory destroys the receiver instance. */
public void destroy() {
shutdown();
}
@@ -93,9 +85,7 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
}
}
/**
* Start this server.
*/
/** Start this server. */
public final void start() {
synchronized (lifecycleMonitor) {
running = true;
@@ -104,9 +94,7 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
onStart();
}
/**
* Stop this server.
*/
/** Stop this server. */
public final void stop() {
synchronized (lifecycleMonitor) {
running = false;
@@ -115,9 +103,7 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
onStop();
}
/**
* Shut down this server.
*/
/** Shut down this server. */
public final void shutdown() {
synchronized (lifecycleMonitor) {
running = false;
@@ -134,18 +120,12 @@ public abstract class AbstractStandaloneMessageReceiver extends SimpleWebService
*/
protected abstract void onActivate() throws Exception;
/**
* Template method invoked when {@link #start()} is invoked.
*/
/** Template method invoked when {@link #start()} is invoked. */
protected abstract void onStart();
/**
* Template method invoked when {@link #stop()} is invoked.
*/
/** Template method invoked when {@link #stop()} is invoked. */
protected abstract void onStop();
/**
* Template method invoked when {@link #shutdown()} is invoked.
*/
/** Template method invoked when {@link #shutdown()} is invoked. */
protected abstract void onShutdown();
}