net.sbbi.upnp.jmx.UPNPMBeanDevicesRequestsHandler.java Source code

Java tutorial

Introduction

Here is the source code for net.sbbi.upnp.jmx.UPNPMBeanDevicesRequestsHandler.java

Source

/*
 * ============================================================================
 *                 The Apache Software License, Version 1.1
 * ============================================================================
 *
 * Copyright (C) 2002 The Apache Software Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modifica-
 * tion, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of  source code must  retain the above copyright  notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution, if any, must
 *    include the following  acknowledgment: "This product includes software
 *    developed by SuperBonBon Industries (http://www.sbbi.net/)."
 *    Alternately, this acknowledgment may appear in the software itself, if
 *    and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "UPNPLib" and "SuperBonBon Industries" must not be
 *    used to endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    info@sbbi.net.
 *
 * 5. Products  derived from this software may not be called 
 *    "SuperBonBon Industries", nor may "SBBI" appear in their name, 
 *    without prior written permission of SuperBonBon Industries.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
 * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT,INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
 * DING, 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.
 *
 * This software  consists of voluntary contributions made by many individuals
 * on behalf of SuperBonBon Industries. For more information on 
 * SuperBonBon Industries, please see <http://www.sbbi.net/>.
 */
package net.sbbi.upnp.jmx;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Class to handle HTTP UPNP requests on UPNPMBeanDevices
 * @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
 * @version 1.0
 */

public class UPNPMBeanDevicesRequestsHandler implements Runnable {

    private final static Log log = LogFactory.getLog(UPNPMBeanDevicesRequestsHandler.class);
    private final static int MAX_HTTP_WORKERS = 10;

    private final static Map instances = new HashMap();

    private Set handledDevices = new HashSet();
    private Set httpWorkers = new HashSet();
    private ServerSocket srv;
    private boolean isRunning = false;

    private InetSocketAddress bindAddress;

    public final static UPNPMBeanDevicesRequestsHandler getInstance(InetSocketAddress bindAddress) {
        String key = bindAddress.toString();
        synchronized (instances) {
            UPNPMBeanDevicesRequestsHandler handler = (UPNPMBeanDevicesRequestsHandler) instances.get(key);
            if (handler == null) {
                handler = new UPNPMBeanDevicesRequestsHandler(bindAddress);
                instances.put(key, handler);
            }
            return handler;
        }
    }

    private UPNPMBeanDevicesRequestsHandler(InetSocketAddress bindAddress) {
        this.bindAddress = bindAddress;
    }

    protected void addUPNPMBeanDevice(UPNPMBeanDevice rootDevice) {
        synchronized (handledDevices) {
            for (Iterator i = handledDevices.iterator(); i.hasNext();) {
                UPNPMBeanDevice registred = (UPNPMBeanDevice) i.next();
                if (registred.getDeviceType().equals(rootDevice.getDeviceType())
                        && registred.getUuid().equals(rootDevice.getUuid())) {
                    // API Use error
                    throw new RuntimeException("An UPNPMBeanDevice object of type " + rootDevice.getDeviceType()
                            + " with uuid " + rootDevice.getUuid()
                            + " is already registred within this class, use a different UPNPMBeanDevice internalId");
                }
            }
            if (handledDevices.size() == 0) {
                Thread runner = new Thread(this, "UPNPMBeanDevicesRequestsHandler " + bindAddress.toString());
                runner.setDaemon(true);
                runner.start();
            }
            handledDevices.add(rootDevice);
            // adding the child devices
            for (Iterator i = rootDevice.getUPNPMBeanChildrens().iterator(); i.hasNext();) {
                handledDevices.add(i.next());
            }
        }
    }

    protected void removeUPNPMBeanDevice(UPNPMBeanDevice rootDevice) {
        synchronized (handledDevices) {
            if (handledDevices.contains(rootDevice)) {
                handledDevices.remove(rootDevice);
                // removing the child devices
                for (Iterator i = rootDevice.getUPNPMBeanChildrens().iterator(); i.hasNext();) {
                    handledDevices.remove(i.next());
                }
                if (handledDevices.size() == 0) {
                    try {
                        isRunning = false;
                        srv.close();
                    } catch (IOException ex) {
                        // do not care
                    }
                }
            }
        }
    }

    protected void notifyWorkerThreadEnd(HttpWorker worker) {
        synchronized (httpWorkers) {
            httpWorkers.remove(worker);
        }
    }

    public void run() {
        try {
            srv = new ServerSocket(bindAddress.getPort(), 200, bindAddress.getAddress());
        } catch (IOException ex) {
            log.error("Error during server socket creation, thread cannot start", ex);
            return;
        }
        isRunning = true;
        while (isRunning) {
            try {
                Socket skt = srv.accept();
                skt.setSoTimeout(30000);
                HttpWorker worker = new HttpWorker(skt, log, handledDevices, this);
                while (httpWorkers.size() >= MAX_HTTP_WORKERS) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ex) {
                        // ignore
                    }
                }
                Thread workerThread = new Thread(worker,
                        "UPNPMBeanDevicesRequestsHandler Http Worker " + httpWorkers.size());
                workerThread.start();
                synchronized (httpWorkers) {
                    httpWorkers.add(worker);
                }
            } catch (IOException ex) {
                if (isRunning) {
                    log.error("Error during client socket creation", ex);
                }
            }
        }
    }

    private class HttpWorker implements Runnable {

        private Socket client;
        private Log logger;
        private Set devices;
        private UPNPMBeanDevicesRequestsHandler handler;

        public HttpWorker(Socket client, Log log, Set handledDevices, UPNPMBeanDevicesRequestsHandler handler) {
            this.client = client;
            this.logger = log;
            this.devices = handledDevices;
            this.handler = handler;
        }

        public void run() {
            try {
                byte[] buffer = new byte[256];
                InputStream in = client.getInputStream();
                StringBuffer request = new StringBuffer();
                int readen = 0;
                String firstReadenData = null;
                while (readen != -1) {
                    readen = in.read(buffer);
                    String data = new String(buffer, 0, readen);
                    if (firstReadenData == null) {
                        firstReadenData = data.toUpperCase();
                    }
                    request.append(data);
                    // either a simple get request
                    String rawRequest = request.toString();
                    if (rawRequest.endsWith("\r\n\r\n") && firstReadenData.startsWith("GET")) {
                        readen = -1;
                    } else if (rawRequest.indexOf("</s:Envelope>") != -1) {
                        // or a post request with content that should end with </s:Envelope>
                        readen = -1;
                    }
                }

                OutputStream out = client.getOutputStream();
                String req = request.toString().trim();
                if (logger.isDebugEnabled())
                    logger.debug("Received message:\n" + req);
                String toWrite = null;
                if (req.length() > 0) {
                    HttpRequest httpReq = new HttpRequest(req);
                    if (httpReq.getHttpCommand() != null && httpReq.getHttpCommandArg() != null
                            && httpReq.getHttpCommandArg().trim().length() > 0) {
                        String cmd = httpReq.getHttpCommand();
                        HttpRequestHandler handler = null;
                        if (cmd.equals("GET")) {
                            handler = HttpGetRequest.getInstance();
                            // TODO implement M-POST
                        } else if (cmd.equals("POST")) {
                            handler = HttpPostRequest.getInstance();
                        } else if (cmd.equals("SUBSCRIBE")) {
                            handler = HttpSubscriptionRequest.getInstance();
                        } else if (cmd.equals("UNSUBSCRIBE")) {
                            handler = HttpSubscriptionRequest.getInstance();
                        }
                        if (handler != null) {
                            toWrite = handler.service(devices, httpReq);
                        }
                    }
                }
                if (toWrite == null) {
                    String content = "<html><head><title>Not found</title></head><body>The requested ressource cannot be found</body></html>";
                    StringBuffer rtr = new StringBuffer();
                    rtr.append("HTTP/1.1 404 Not Found\r\n");
                    rtr.append("CONTENT-LENGTH: ").append(content.length()).append("\r\n");
                    rtr.append("CONTENT-TYPE: text/html\r\n\r\n");
                    rtr.append(content);
                    toWrite = rtr.toString();
                }
                if (logger.isDebugEnabled())
                    logger.debug("Sending response :\n" + toWrite);
                out.write(toWrite.getBytes());
                out.flush();
                out.close();
                in.close();
                client.close();
            } catch (IOException ex) {
                logger.error("IO Exception occured during client serving", ex);
            } catch (Throwable t) {
                logger.error("Unexpected Exception occured during client serving", t);
            } finally {
                handler.notifyWorkerThreadEnd(this);
            }
        }
    }
}