| /** |
| * 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. |
| */ |
| package org.apache.openejb.server.httpd; |
| |
| import org.apache.openejb.OpenEJBException; |
| import org.apache.openejb.core.ParentClassLoaderFinder; |
| import org.apache.openejb.loader.Options; |
| import org.apache.openejb.loader.SystemInstance; |
| import org.apache.openejb.server.ServiceException; |
| import org.apache.openejb.server.context.RequestInfos; |
| import org.apache.openejb.server.httpd.session.SessionManager; |
| import org.apache.openejb.server.stream.CountingInputStream; |
| import org.apache.openejb.server.stream.CountingOutputStream; |
| import org.apache.openejb.util.LogCategory; |
| import org.apache.openejb.util.Logger; |
| import org.apache.openejb.util.OptionsLog; |
| |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.net.Socket; |
| import java.net.SocketException; |
| import java.net.URI; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| /** |
| * This is the main class for the web administration. It takes care of the |
| * processing from the browser, sockets and threading. |
| * |
| * @since 11/25/2001 |
| */ |
| public class OpenEJBHttpServer implements HttpServer { |
| |
| private static final Logger log = Logger.getInstance(LogCategory.HTTPSERVER, "org.apache.openejb.util.resources"); |
| |
| private HttpListener listener; |
| private Set<Output> print; |
| private boolean indent; |
| private boolean countStreams; |
| |
| public OpenEJBHttpServer() { |
| this(null); |
| } |
| |
| public static HttpListenerRegistry getHttpListenerRegistry() { |
| final SystemInstance systemInstance = SystemInstance.get(); |
| HttpListenerRegistry registry = systemInstance.getComponent(HttpListenerRegistry.class); |
| if (registry == null) { |
| registry = new HttpListenerRegistry(); |
| systemInstance.setComponent(HttpListenerRegistry.class, registry); |
| } |
| return registry; |
| } |
| |
| public OpenEJBHttpServer(final HttpListener listener) { |
| if (SystemInstance.get().getComponent(SessionManager.class) == null) { |
| SystemInstance.get().setComponent(SessionManager.class, new SessionManager()); |
| } |
| this.listener = new OpenEJBHttpRegistry.ClassLoaderHttpListener( |
| listener == null ? getHttpListenerRegistry() : listener, ParentClassLoaderFinder.Helper.get()); |
| } |
| |
| public static boolean isTextXml(final Map<String, List<String>> headers) { |
| final Collection<String> contentType = headers.get("Content-Type"); |
| if (contentType == null) { |
| return false; |
| } |
| for (final String current : contentType) { |
| if (current.contains("text/xml")) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public HttpListener getListener() { |
| return listener; |
| } |
| |
| @Override |
| public void service(final Socket socket) throws ServiceException, IOException { |
| /** |
| * The InputStream used to receive incoming messages from the client. |
| */ |
| InputStream in = null; |
| /** |
| * The OutputStream used to send outgoing response messages to the client. |
| */ |
| OutputStream out = null; |
| |
| boolean close = true; |
| try { |
| RequestInfos.initRequestInfo(socket); |
| |
| if (countStreams) { |
| in = new CountingInputStream(socket.getInputStream()); |
| out = new CountingOutputStream(socket.getOutputStream()); |
| } else { |
| in = socket.getInputStream(); |
| out = socket.getOutputStream(); |
| } |
| |
| //TODO: if ssl change to https |
| final URI socketURI = new URI("http://" + socket.getLocalAddress().getHostAddress() + ":" + socket.getLocalPort()); |
| close = processRequest(socket, socketURI, in, out); |
| |
| } catch (final Throwable e) { |
| log.error("Unexpected error", e); |
| } finally { |
| if (close) { |
| if (out != null) { |
| try { |
| out.flush(); |
| } catch (Throwable e) { |
| //Ignore |
| } |
| try { |
| out.close(); |
| } catch (Throwable e) { |
| //Ignore |
| } |
| } |
| |
| if (in != null) { |
| try { |
| in.close(); |
| } catch (Throwable e) { |
| //Ignore |
| } |
| } |
| |
| try { |
| socket.close(); |
| } catch (Throwable e) { |
| log.error("Encountered problem while closing connection with client: " + e.getMessage()); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void service(final InputStream in, final OutputStream out) throws ServiceException, IOException { |
| throw new UnsupportedOperationException("Method not implemented: service(InputStream in, OutputStream out)"); |
| } |
| |
| @Override |
| public void init(final Properties props) throws Exception { |
| final Options options = new Options(props); |
| options.setLogger(new OptionsLog(log)); |
| print = options.getAll("print", OpenEJBHttpServer.Output.class); |
| indent = print.size() > 0 && options.get("" + |
| "" + |
| ".xml", false); |
| countStreams = options.get("stream.count", false); |
| } |
| |
| public static enum Output { |
| REQUEST, |
| RESPONSE |
| } |
| |
| @Override |
| public void start() throws ServiceException { |
| } |
| |
| @Override |
| public void stop() throws ServiceException { |
| OpenEJBAsyncContext.destroy(); |
| final SessionManager component = SystemInstance.get().getComponent(SessionManager.class); |
| if (component != null) { |
| component.destroy(); |
| } |
| } |
| |
| @Override |
| public String getName() { |
| return "httpd"; |
| } |
| |
| @Override |
| public int getPort() { |
| return 0; |
| } |
| |
| @Override |
| public String getIP() { |
| return ""; |
| } |
| |
| /** |
| * takes care of processing requests and creating the webadmin ejb's |
| * |
| * @param in the input stream from the browser |
| * @param out the output stream to the browser |
| */ |
| private boolean processRequest(final Socket socket, final URI socketURI, final InputStream in, final OutputStream out) { |
| HttpResponseImpl response = null; |
| try { |
| response = process(socket, socketURI, in); |
| return response != null; |
| } catch (final Throwable t) { |
| log.error(t.getMessage(), t); |
| response = HttpResponseImpl.createError(t.getMessage(), t); |
| return true; |
| } finally { |
| try { |
| if (response != null) { |
| response.writeMessage(out, false); |
| if (print.size() > 0 && print.contains(Output.RESPONSE)) { |
| response.writeMessage(new LoggerOutputStream(log, "debug"), indent); |
| } |
| } |
| } catch (final Throwable t2) { |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Could not write response", t2); |
| } else { |
| //SocketException is something a client can cause, so do not log it (potential DOS) |
| if (!SocketException.class.isInstance(t2)) { |
| log.warning("Could not write response:" + t2); |
| } |
| } |
| |
| } |
| } |
| } |
| |
| private HttpResponseImpl process(final Socket socket, final URI socketURI, final InputStream in) throws OpenEJBException { |
| final HttpRequestImpl req = new HttpRequestImpl(socketURI); |
| final HttpResponseImpl res = new HttpResponseImpl(); |
| |
| try { |
| if (!req.readMessage(in)) { |
| return res; |
| } |
| |
| if (print.size() > 0 && print.contains(Output.REQUEST)) { |
| req.print(log, indent); |
| } |
| |
| res.setRequest(req); |
| } catch (Throwable t) { |
| res.setCode(400); |
| res.setResponseString("Could not read the request"); |
| try { |
| res.getWriter().println(t.getMessage()); |
| t.printStackTrace(res.getWriter()); |
| } catch (IOException e) { |
| // no-op |
| } |
| log.error("BAD REQUEST", t); |
| throw new OpenEJBException("Could not read the request.\n" + t.getClass().getName() + ":\n" + t.getMessage(), t); |
| } |
| |
| final URI uri; |
| String location = null; |
| try { |
| uri = req.getURI(); |
| location = uri.getPath(); |
| final int querry = location.indexOf("?"); |
| if (querry != -1) { |
| location = location.substring(0, querry); |
| } |
| } catch (Throwable t) { |
| throw new OpenEJBException("Could not determine the module " + location + "\n" + t.getClass().getName() + ":\n" + t.getMessage()); |
| } |
| |
| try { |
| req.setAttribute("openejb_response", res); |
| req.setAttribute("openejb_socket", socket); |
| listener.onMessage(req, res); |
| } catch (Throwable t) { |
| throw new OpenEJBException("Error occurred while executing the module " + location + "\n" + t.getClass().getName() + ":\n" + t.getMessage(), t); |
| } |
| |
| final boolean async = "true".equals(req.getAttribute("openejb_async")); |
| return !async ? res : null; |
| } |
| |
| public static String reformat(final String raw) { |
| if (raw.length() == 0) { |
| return raw; |
| } |
| |
| try { |
| final TransformerFactory factory = TransformerFactory.newInstance(); |
| // bugged in some XML implementation |
| // should we use another implementation? |
| //factory.setAttribute("indent-number", 2); |
| |
| final Transformer transformer = factory.newTransformer(); |
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); |
| transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); |
| |
| final StreamResult result = new StreamResult(new StringWriter()); |
| |
| transformer.transform(new StreamSource(new StringReader(raw)), result); |
| |
| return result.getWriter().toString(); |
| } catch (TransformerException e) { |
| e.printStackTrace(); |
| return raw; |
| } |
| } |
| |
| private static class LoggerOutputStream extends OutputStream { |
| |
| private final Logger logger; |
| private final String level; |
| |
| public LoggerOutputStream(final Logger log, final String lvl) { |
| logger = log; |
| level = lvl; |
| } |
| |
| @Override |
| public void write(final int b) throws IOException { |
| logger.log(level, Character.toString((char) b)); |
| } |
| |
| @Override // shortcut for String - because we know what we have ;) |
| public void write(final byte[] b) throws IOException { |
| logger.log(level, new String(b)); |
| } |
| } |
| } |