Package org.knopflerfish.bundle.http

Source Code of org.knopflerfish.bundle.http.SocketListener

/*
* Copyright (c) 2003-2008, KNOPFLERFISH project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
*   copyright notice, this list of conditions and the following
*   disclaimer in the documentation and/or other materials
*   provided with the distribution.
*
* - Neither the name of the KNOPFLERFISH project nor the names of its
*   contributors may be used to endorse or promote products derived
*   from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.knopflerfish.bundle.http;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import org.knopflerfish.service.log.LogRef;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

public class SocketListener implements Runnable, ServiceTrackerCustomizer {

    // private constants

    private final static String zeroAddress = "0.0.0.0";

    // private fields

    private final HttpConfigWrapper httpConfig;

    private final LogRef log;

    private final TransactionManager transactionManager;

    private final BundleContext bc;

    private final HttpServer httpServer;

    private ServiceTracker securityTracker = null;

    private int port = -1;

    private String host = null;

    private int maxConnections = -1;

    private boolean isSecure = false;

    private boolean isEnabled = false; // initial state of this object is

    // disabled
    private Boolean requireClientAuth = null;

    private boolean done = false;

    private ServerSocket socket = null;

    private Thread thread = null;

    // constructors

    public SocketListener(final HttpConfigWrapper httpConfig,
                          final LogRef log,
                          final TransactionManager transactionManager,
                          final BundleContext bc,
                          final HttpServer httpServer)
  {

        this.httpConfig = httpConfig;
        this.log = log;
        this.transactionManager = transactionManager;
        this.bc = bc;
        this.httpServer = httpServer;
    }

    public void updated() throws ConfigurationException {

        // the following if statements prevents unnecessary calls to init
        // (nothing
        // changed)
        if (isSecure == httpConfig.isSecure()
                && (requireClientAuth != null)
                && (requireClientAuth.booleanValue() == httpConfig
                        .requireClientAuth()) && port == httpConfig.getPort()
                && httpConfig.getHost().equals(host)
                && httpConfig.getMaxConnections() == maxConnections
                && isEnabled == httpConfig.isEnabled()) {
            return;
        }

        isSecure = httpConfig.isSecure();
        requireClientAuth = new Boolean(httpConfig.requireClientAuth());
        port = httpConfig.getPort();
        host = httpConfig.getHost();
        maxConnections = httpConfig.getMaxConnections();
        isEnabled = httpConfig.isEnabled();

        destroy();

        if (!isEnabled) {
            return;
        }

        /**
         * We want to be able to do either HTTPS or HTTP. The latter would
         * always be executed synchronously, e.g. in this invocation. This might
         * not be the case for HTTPs, which might happen on different threads.
         * Therefore, try to throw any ConfigurationEx as early as possible
         */
        if ((port < 0) || (port > 0xFFFF)) {
            throw new ConfigurationException(
                    (httpConfig.isSecure() ? HttpConfig.HTTPS_PORT_KEY
                            : HttpConfig.HTTP_PORT_KEY), "invalid value="
                            + port);

        }
        if (maxConnections < 1) {
            throw new ConfigurationException("maxConnections", "invalid value="
                    + maxConnections);

        }

        if (!isSecure) {
            // for HTTP create the socket right away AND start
            try {
                if (log.doDebug())
                    log.debug("Creating socket");
                if (host == null || host.length() == 0) {
                    socket = new ServerSocket(port, maxConnections);
                } else {
                    try {
                        socket = new ServerSocket(port, maxConnections,
                                InetAddress.getByName(host));
                    } catch (UnknownHostException uhe) {
                        socket = new ServerSocket(port, maxConnections);
                    }
                }

                init();

            } catch (Exception e) {
                if (log.doDebug())
                    log.debug("Exception creating HTTP Socket", e);
                throw new ConfigurationException(
                        "Exception creating HTTP Socket", e.toString());
            }

        } else // secure case, can not create socket by myself, need to get
                // service
        {
            securityTracker = new ServiceTracker(this.bc,
                    "javax.net.ssl.SSLServerSocketFactory", this);
            securityTracker.open();
        }
    }

    public Object addingService(ServiceReference sRef) {
        // TE this class must not explicitly reference SSLServerFactory.
        Object factory = null;

        if (this.socket != null) {
            if (log.doWarn())
                log.warn("SEVERAL  SSLServerSocketFactories are available,"
                         +" selection random");
            return null; // do not track
        }

        factory = this.bc.getService(sRef);

        // find the two methods using reflection
        Method create2 = null;
        Method create3 = null;

        try {
            create2 = factory.getClass().getMethod("createServerSocket",
                    new Class[] { int.class, int.class });
            create3 = factory.getClass().getMethod("createServerSocket",
                    new Class[] { int.class, int.class, InetAddress.class });

        } catch (Exception cmethE) {
            log.error("not an SSL factory, or no access : " + factory, cmethE);

        }

        if (host == null || host.length() == 0) {
            try {
                socket = (ServerSocket) create2.invoke(factory, new Object[] {
                        new Integer(port), new Integer(maxConnections) });

            } catch (Exception ex) {
                ex.printStackTrace();
                log.error("creating ssl socket on server:", ex);
            }

        } else {
            try {
                try {
                    socket = (ServerSocket) create3.invoke(factory,
                            new Object[] { new Integer(port),
                                    new Integer(maxConnections),
                                    InetAddress.getByName(host) });

                } catch (UnknownHostException uhe) {
                    socket = (ServerSocket) create2.invoke(factory,
                            new Object[] { new Integer(port),
                                    new Integer(maxConnections) });
                }

            } catch (Exception ex) {
                log.error("creating socket ", ex);
            }

        }

        if (socket != null) {
            try {
                Class sslSockClass = Class
                        .forName("javax.net.ssl.SSLServerSocket");
                Method auth = sslSockClass.getMethod("setNeedClientAuth",
                        new Class[] { boolean.class });
                auth.invoke(socket, new Object[] { requireClientAuth });

            } catch (Exception exc) {
                log.error(exc.toString());
            }
        }

        if (socket != null) {
            try {
                init();
            } catch (Exception e) {
                log.error("Can not initialize", e);
                socket = null;
            }
        }

        if (socket == null) {
            this.bc.ungetService(sRef);
            factory = null;

        }

        return factory;
    }

    public void modifiedService(ServiceReference arg0, Object arg1) {

    }

    public void removedService(ServiceReference sRef, Object arg1) {
        if (log.doDebug())
            log.debug("SSLFactory Security service was removed.");

        uninit();

        this.bc.ungetService(sRef);

    }

    private synchronized void init() {
        // socket exists, otherwise not called
        done = false;

        port = socket.getLocalPort();
        if (port!=httpConfig.getPort()) {
          // Update config to reflect the actual port used (if port was
          // specified as 0 this will make a difference).
          httpConfig.setPort(port);

          if (isSecure) {
            // Make sure that the service property port.https is
            // correct. Only needed for https, since init() is called
            // asynchroneous in that case.
            httpServer.doHttpReg();
          }
        }

        String sch = httpConfig.getScheme().toUpperCase();

        if (log.doInfo())
            log.info(sch + " server started on port " + port);

        thread = new Thread(this, sch + " server:" + port);
        thread.start();

    }

    private synchronized void uninit() {
        done = true;

        Thread[] threads = new Thread[transactionManager.activeCount()];
        transactionManager.enumerate(threads);
        for (int i = 0; i < threads.length; i++)
            if (threads[i] != null)
                threads[i].interrupt();

        threads = new Thread[transactionManager.activeCount()];
        transactionManager.enumerate(threads);
        for (int i = 0; i < threads.length; i++) {
            if (threads[i] != null) {
                try {
                    threads[i].join(5000);
                } catch (InterruptedException ignore) {
                }
                if (threads[i].isAlive()) {
                    // TBD, threads[i].stop();
                    log.error("Thread " + threads[i] + ", refuse to stop");
                }
            }
        }

        if (thread != null)
            thread.interrupt();

        if (socket != null) {
            // Try different ways to find out local address.
            int port = socket.getLocalPort();
            InetAddress address = socket.getInetAddress();
            if (address.getHostAddress().equals(zeroAddress)) {
                try {
                    address = InetAddress.getLocalHost();
                } catch (UnknownHostException ignore) {
                    try {
                        address = InetAddress.getByName("localhost");
                    } catch (UnknownHostException ignore2) {
                        try {
                            address = InetAddress.getByName("127.0.0.1");
                        } catch (UnknownHostException e) {
                            address = null;
                            log.error("Failed to get local address", e);
                        }
                    }
                }
            }
            try {
                if (address != null) {
                    (new Socket(address, port)).close();
                }
            } catch (IOException ignore) {
                // Socket could already be closed?!
            } finally {
                socket = null;
            }
        }

        if (thread != null) {
            try {
                thread.join(60000);
            } catch (InterruptedException ignore) {
            } finally {
                if (thread.isAlive()) {
                    log.error("Failed to stop socket listener thread, " + thread);
                }
                thread = null;
            }
        }
    }

    public void destroy() {

        if (!httpConfig.isSecure()) {
            uninit();

        } else {
            ServiceTracker tempTr = this.securityTracker;
            this.securityTracker = null;
            if (tempTr != null) {
                try {
                    tempTr.close();

                } catch (Exception excpt) {
                }
            }
        }
    }

    public void run() {

        ServerSocket socket = this.socket;
        Socket client = null;

        while (!done) {

            try {

                while (transactionManager.activeCount() >= httpConfig
                        .getMaxConnections()) {
                    try {
                        Thread.sleep(50);
                        if (done)
                            break;
                    } catch (Exception e) {
                    }
                }

                if (!done) {
                    client = socket.accept();
                    if (done) {
                        client.close();
                        break;
                    }
                }

                transactionManager.startTransaction(client, httpConfig);

            } catch (InterruptedIOException iioe) {
                if (!done && log.doDebug())
                    log.debug("Communication error on "
                            + (host != null ? (host + ":") : "") + port, iioe);
            } catch (IOException ioe) {
                if (!done && log.doDebug())
                    log.debug("Communication error on "
                            + (host != null ? (host + ":") : "") + port, ioe);
            } catch (ThreadDeath td) {
                throw td;
            } catch (Throwable t) {
                if (!done && log.doDebug())
                    log.debug("Internal error on"
                            + (host != null ? (host + ":") : "") + port, t);
            }

        }

        try {
            if (socket != null)
                socket.close();
        } catch (IOException ignore) {
        }
        socket = null;
    }

} // SocketListener
TOP

Related Classes of org.knopflerfish.bundle.http.SocketListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.