/* * 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.vysper.xmpp.extension.websockets; import java.io.IOException; import org.apache.vysper.xmpp.server.Endpoint; import org.apache.vysper.xmpp.server.ServerRuntimeContext; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Allows HTTP clients to communicate via the WebSocket protocol with Vysper. * See http://tools.ietf.org/html/draft-moffitt-xmpp-over-websocket-00 * * @author The Apache MINA Project (dev@mina.apache.org) */ public class WebSocketEndpoint implements Endpoint { protected final static Logger logger = LoggerFactory.getLogger(WebSocketEndpoint.class); protected ServerRuntimeContext serverRuntimeContext; protected int port = 8080; protected Server server; protected boolean isSSLEnabled; protected String sslKeystorePath; protected String sslKeystorePassword; protected String contextPath = "/"; /** * {@inheritDoc} */ public void setServerRuntimeContext(ServerRuntimeContext serverRuntimeContext) { this.serverRuntimeContext = serverRuntimeContext; } /** * Set the port on which the endpoint will listen for incoming traffic. * Defaults to 8080. * @param port The TCP/IP listener port */ public void setPort(int port) { this.port = port; } /** * Configures the SSL keystore * <p> * Required if SSL is enabled. Also, setting the keystore password is * required. * @param keystorePath The path to the Java keystore * @param password The password for the Java keystore */ public void setSSLCertificateKeystore(String keystorePath, String password) { sslKeystorePath = keystorePath; sslKeystorePassword = password; } /** * Enables/disables SSL for this endpoint. * <p> * If SSL is enabled it requires SSL certificate information that can be * configured with {@link #setSSLCertificateInfo(String, String)} * @param value */ public void setSSLEnabled(boolean value) { isSSLEnabled = value; } /** * Determines the context URI where the websocket transport will be accessible. * The default is as 'root context' under '/'. * @param contextPath */ public void setContextPath(String contextPath) { if (contextPath == null) contextPath = "/"; this.contextPath = contextPath; } /** * create a basic Jetty server including a connector on the configured port * override in subclass to create a different kind of setup or to reuse an existing instance * @return */ protected Server createJettyServer() { Server server = new Server(); Connector connector; if (isSSLEnabled) { SslSelectChannelConnector sslConnector = new SslSelectChannelConnector(); sslConnector.setKeystore(sslKeystorePath); sslConnector.setPassword(sslKeystorePassword); sslConnector.setKeyPassword(sslKeystorePassword); connector = sslConnector; } else { connector = new SelectChannelConnector(); } connector.setPort(port); server.setConnectors(new Connector[] { connector }); return server; } /** * create handler for XMPP over websockets. * for a different handler setup, override in a subclass. * for more than one handler, add them to a org.eclipse.jetty.server.handler.ContextHandlerCollection * and return the collection * @return */ protected Handler createHandler() { ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS); servletContext.setContextPath(contextPath); XmppWebSocketServlet wsServlet = new XmppWebSocketServlet(serverRuntimeContext); servletContext.addServlet(new ServletHolder(wsServlet), "/ws"); return servletContext; } /** * {@inheritDoc} * * @throws RuntimeException a wrapper of the possible {@link java.lang.Exception} that Jetty can throw at start-up */ public void start() throws IOException { server = createJettyServer(); Handler wsHandler = createHandler(); Handler existingHandler = server.getHandler(); if(existingHandler != null && existingHandler instanceof HandlerCollection) { ((HandlerCollection)existingHandler).addHandler(wsHandler); } else { server.setHandler(wsHandler); } try { server.start(); } catch (Exception e) { throw new RuntimeException(e); } } /** * {@inheritDoc} */ public void stop() { try { server.stop(); } catch (Exception e) { logger.warn("Could not stop the Jetty server", e); } } }