Java tutorial
/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.commons.vfs2.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Date; import java.util.Locale; import java.util.concurrent.Executors; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import org.apache.commons.httpclient.util.DateUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpException; import org.apache.http.HttpHeaders; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpResponseInterceptor; import org.apache.http.HttpStatus; import org.apache.http.MethodNotSupportedException; import org.apache.http.entity.ContentType; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.nio.DefaultHttpServerIODispatch; import org.apache.http.impl.nio.DefaultNHttpServerConnection; import org.apache.http.impl.nio.DefaultNHttpServerConnectionFactory; import org.apache.http.impl.nio.SSLNHttpServerConnectionFactory; import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor; import org.apache.http.nio.NHttpConnection; import org.apache.http.nio.NHttpConnectionFactory; import org.apache.http.nio.NHttpServerConnection; import org.apache.http.nio.entity.NFileEntity; import org.apache.http.nio.entity.NStringEntity; import org.apache.http.nio.protocol.BasicAsyncRequestConsumer; import org.apache.http.nio.protocol.BasicAsyncResponseProducer; import org.apache.http.nio.protocol.HttpAsyncExchange; import org.apache.http.nio.protocol.HttpAsyncRequestConsumer; import org.apache.http.nio.protocol.HttpAsyncRequestHandler; import org.apache.http.nio.protocol.HttpAsyncRequestHandlerRegistry; import org.apache.http.nio.protocol.HttpAsyncService; import org.apache.http.nio.reactor.IOEventDispatch; import org.apache.http.nio.reactor.IOReactorException; import org.apache.http.nio.reactor.ListeningIOReactor; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; import org.apache.http.params.SyncBasicHttpParams; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpProcessor; import org.apache.http.protocol.ImmutableHttpProcessor; import org.apache.http.protocol.ResponseConnControl; import org.apache.http.protocol.ResponseContent; import org.apache.http.protocol.ResponseDate; import org.apache.http.protocol.ResponseServer; /** * Adapted from org.apache.http.examples.nio.NHttpServer. * * <p> * HTTP/1.1 file server based on the non-blocking I/O model and capable of direct channel (zero copy) data transfer. * </p> * <p> * Please note the purpose of this application is demonstrate the usage of HttpCore APIs. It is NOT intended to * demonstrate the most efficient way of building an HTTP server. * </p> * * @version $Id$ * @since 2.1 */ public class NHttpServer { static class HttpFileHandler implements HttpAsyncRequestHandler<HttpRequest> { private final File docRoot; public HttpFileHandler(final File docRoot) { super(); this.docRoot = docRoot; } @Override public void handle(final HttpRequest request, final HttpAsyncExchange httpexchange, final HttpContext context) throws HttpException, IOException { final HttpResponse response = httpexchange.getResponse(); this.handleInternal(request, response, context); httpexchange.submitResponse(new BasicAsyncResponseProducer(response)); } private void handleInternal(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException { final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH); if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) { throw new MethodNotSupportedException(method + " method not supported"); } final String target = request.getRequestLine().getUri(); final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8")); if (!file.exists()) { response.setStatusCode(HttpStatus.SC_NOT_FOUND); final NStringEntity entity = new NStringEntity( "<html><body><h1>File" + file.getPath() + " not found</h1></body></html>", ContentType.create("text/html", "UTF-8")); response.setEntity(entity); NHttpServer.debug("File " + file.getPath() + " not found"); } else if (!file.canRead()) { response.setStatusCode(HttpStatus.SC_FORBIDDEN); final NStringEntity entity = new NStringEntity("<html><body><h1>Access denied</h1></body></html>", ContentType.create("text/html", "UTF-8")); response.setEntity(entity); NHttpServer.debug("Cannot read file " + file.getPath()); } else { final NHttpConnection conn = (NHttpConnection) context .getAttribute(ExecutionContext.HTTP_CONNECTION); response.setStatusCode(HttpStatus.SC_OK); final NFileEntity body = new NFileEntity(file, ContentType.create("text/html")); response.setEntity(body); if (!response.containsHeader(HttpHeaders.LAST_MODIFIED)) { response.addHeader(HttpHeaders.LAST_MODIFIED, DateUtil.formatDate(new Date(file.lastModified()))); } NHttpServer.debug(conn + ": serving file " + file.getPath()); } } @Override public HttpAsyncRequestConsumer<HttpRequest> processRequest(final HttpRequest request, final HttpContext context) { // Buffer request content in memory for simplicity return new BasicAsyncRequestConsumer(); } } static final boolean Debug = false; private static void debug(final String s) { if (Debug) { System.out.println(s); } } public static void main(final String[] args) throws Exception { new NHttpServer().run(Integer.valueOf(args[0]), new File(args[1]), 0); } volatile ListeningIOReactor ioReactor; public boolean run(final int port, final File docRoot, final long waitMillis) throws IOReactorException, InterruptedException { Executors.newSingleThreadExecutor().execute(new Runnable() { @Override public void run() { try { NHttpServer.this.runBlock(port, docRoot); } catch (final IOReactorException e) { throw new IllegalStateException(e); } catch (final UnrecoverableKeyException e) { throw new IllegalStateException(e); } catch (final KeyStoreException e) { throw new IllegalStateException(e); } catch (final NoSuchAlgorithmException e) { throw new IllegalStateException(e); } catch (final CertificateException e) { throw new IllegalStateException(e); } catch (final IOException e) { throw new IllegalStateException(e); } catch (final KeyManagementException e) { throw new IllegalStateException(e); } } }); return this.waitForServerStartup(port, waitMillis); } public void runBlock(final int port, final File docRoot) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException { if (docRoot == null) { throw new IllegalArgumentException("No doc root specified."); } // HTTP parameters for the server final HttpParams params = new SyncBasicHttpParams(); params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000) .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpTest/1.1"); // Create HTTP protocol processing chain final HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] { // Use standard server-side protocol interceptors new ResponseDate(), new ResponseServer(), new ResponseContent(), new ResponseConnControl() }); // Create request handler registry final HttpAsyncRequestHandlerRegistry reqistry = new HttpAsyncRequestHandlerRegistry(); // Register the default handler for all URIs reqistry.register("*", new HttpFileHandler(docRoot)); // Create server-side HTTP protocol handler final HttpAsyncService protocolHandler = new HttpAsyncService(httpproc, new DefaultConnectionReuseStrategy(), reqistry, params) { @Override public void closed(final NHttpServerConnection conn) { NHttpServer.debug(conn + ": connection closed"); super.closed(conn); } @Override public void connected(final NHttpServerConnection conn) { NHttpServer.debug(conn + ": connection open"); super.connected(conn); } }; // Create HTTP connection factory NHttpConnectionFactory<DefaultNHttpServerConnection> connFactory; if (port == 8443) { // Initialize SSL context final ClassLoader cl = NHttpServer.class.getClassLoader(); final URL url = cl.getResource("my.keystore"); if (url == null) { NHttpServer.debug("Keystore not found"); System.exit(1); } final KeyStore keystore = KeyStore.getInstance("jks"); keystore.load(url.openStream(), "secret".toCharArray()); final KeyManagerFactory kmfactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmfactory.init(keystore, "secret".toCharArray()); final KeyManager[] keymanagers = kmfactory.getKeyManagers(); final SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(keymanagers, null, null); connFactory = new SSLNHttpServerConnectionFactory(sslcontext, null, params); } else { connFactory = new DefaultNHttpServerConnectionFactory(params); } // Create server-side I/O event dispatch final IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory); // Create server-side I/O reactor this.ioReactor = new DefaultListeningIOReactor(); try { // Listen of the given port this.ioReactor.listen(new InetSocketAddress(port)); // Ready to go! this.ioReactor.execute(ioEventDispatch); } catch (final InterruptedIOException ex) { System.err.println("Interrupted"); } catch (final IOException e) { System.err.println("I/O error: " + e.getMessage()); } NHttpServer.debug("Shutdown"); } public void stop() throws IOException { if (this.ioReactor != null) { this.ioReactor.shutdown(2000); } } private boolean waitForServerStartup(final int port, final long waitMillis) throws InterruptedException { final long endWait = System.currentTimeMillis() + waitMillis; final String urlSpec = "http://localhost:" + port; try { final URL url = new URL(urlSpec); InputStream inputStream = null; while (System.currentTimeMillis() < endWait && inputStream == null) { try { inputStream = url.openStream(); if (inputStream != null) { IOUtils.closeQuietly(inputStream); return true; } } catch (final IOException e) { // ignore // debug("While waiting: " + e); // e.printStackTrace(); } Thread.sleep(100); } } catch (final MalformedURLException e) { throw new IllegalStateException("Error in test code for URL " + urlSpec); } return false; } }