001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.cometd;
018    
019    import java.util.HashMap;
020    import java.util.Map;
021    
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.impl.DefaultComponent;
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.mortbay.cometd.AbstractBayeux;
027    import org.mortbay.cometd.continuation.ContinuationCometdServlet;
028    import org.mortbay.jetty.Connector;
029    import org.mortbay.jetty.Server;
030    import org.mortbay.jetty.handler.ContextHandlerCollection;
031    import org.mortbay.jetty.nio.SelectChannelConnector;
032    import org.mortbay.jetty.security.SslSocketConnector;
033    import org.mortbay.jetty.servlet.Context;
034    import org.mortbay.jetty.servlet.ServletHolder;
035    
036    /**
037     * Component for Jetty Cometd
038     * 
039     * @version $Revision:520964 $
040     */
041    public class CometdComponent extends DefaultComponent {
042        private static final transient Log LOG = LogFactory.getLog(CometdComponent.class);
043    
044        private final Map<String, ConnectorRef> connectors = new HashMap<String, ConnectorRef>();
045    
046        private Server server;
047        private String sslKeyPassword;
048        private String sslPassword;
049        private String sslKeystore;
050        private SslSocketConnector sslSocketConnector;
051    
052        class ConnectorRef {
053            Connector connector;
054            ContinuationCometdServlet servlet;
055            int refCount;
056    
057            public ConnectorRef(Connector connector,
058                    ContinuationCometdServlet servlet) {
059                this.connector = connector;
060                this.servlet = servlet;
061                increment();
062            }
063    
064            public int increment() {
065                return ++refCount;
066            }
067    
068            public int decrement() {
069                return --refCount;
070            }
071        }
072    
073        public CometdComponent() {
074        }
075    
076        @Override
077        protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
078            setProperties(this, parameters);
079            return new CometdEndpoint(this, uri, remaining, parameters);
080        }
081    
082        /**
083         * Connects the URL specified on the endpoint to the specified processor.
084         */
085        public void connect(CometdProducerConsumer prodcon) throws Exception {
086            // Make sure that there is a connector for the requested endpoint.
087            CometdEndpoint endpoint = (CometdEndpoint) prodcon.getEndpoint();
088            String connectorKey = endpoint.getProtocol() + ":" + endpoint.getUri().getHost() + ":" + endpoint.getPort();
089    
090            synchronized (connectors) {
091                ConnectorRef connectorRef = connectors.get(connectorKey);
092                if (connectorRef == null) {
093                    Connector connector;
094                    if ("cometds".equals(endpoint.getProtocol())) {
095                        connector = getSslSocketConnector();
096                    } else {
097                        connector = new SelectChannelConnector();
098                    }
099                    connector.setPort(endpoint.getPort());
100                    connector.setHost(endpoint.getUri().getHost());
101                    if ("localhost".equalsIgnoreCase(endpoint.getUri().getHost())) {
102                        LOG.warn("You use localhost interface! It means that no external connections will be available."
103                                 + " Don't you want to use 0.0.0.0 instead (all network interfaces)?");
104                    }
105                    getServer().addConnector(connector);
106    
107                    ContinuationCometdServlet servlet = createServletForConnector(connector, endpoint);
108                    connectorRef = new ConnectorRef(connector, servlet);
109                    connector.start();
110    
111                    connectors.put(connectorKey, connectorRef);
112                } else {
113                    connectorRef.increment();
114                }
115                AbstractBayeux bayeux = connectorRef.servlet.getBayeux();
116                bayeux.setJSONCommented(endpoint.isJsonCommented());
117                prodcon.setBayeux(bayeux);
118            }
119        }
120    
121        /**
122         * Disconnects the URL specified on the endpoint from the specified
123         * processor.
124         */
125        public void disconnect(CometdProducerConsumer prodcon) throws Exception {
126            CometdEndpoint endpoint = prodcon.getEndpoint();
127    
128            String connectorKey = endpoint.getProtocol() + ":" + endpoint.getUri().getHost() + ":" + endpoint.getPort();
129    
130            synchronized (connectors) {
131                ConnectorRef connectorRef = connectors.get(connectorKey);
132                if (connectorRef != null) {
133                    if (connectorRef.decrement() == 0) {
134                        getServer().removeConnector(connectorRef.connector);
135                        connectorRef.connector.stop();
136                        connectors.remove(connectorKey);
137                    }
138                }
139            }
140        }
141    
142        protected ContinuationCometdServlet createServletForConnector(Connector connector, CometdEndpoint endpoint) throws Exception {
143            ContinuationCometdServlet servlet = new ContinuationCometdServlet();
144    
145            Context context = new Context(server, "/", Context.NO_SECURITY | Context.NO_SESSIONS);
146            context.setConnectorNames(new String[] {connector.getName()});
147    
148            ServletHolder holder = new ServletHolder();
149            holder.setServlet(servlet);
150            context.setResourceBase(endpoint.getResourceBase());
151            context.addServlet(holder, "/cometd/*");
152            context.addServlet("org.mortbay.jetty.servlet.DefaultServlet", "/");
153    
154            connector.start();
155            context.start();
156    
157            holder.setInitParameter("timeout", Integer.toString(endpoint.getTimeout()));
158            holder.setInitParameter("interval", Integer.toString(endpoint.getInterval()));
159            holder.setInitParameter("maxInterval", Integer.toString(endpoint.getMaxInterval()));
160            holder.setInitParameter("multiFrameInterval", Integer.toString(endpoint.getMultiFrameInterval()));
161            holder.setInitParameter("JSONCommented", Boolean.toString(endpoint.isJsonCommented()));
162            holder.setInitParameter("logLevel", Integer.toString(endpoint.getLogLevel()));
163    
164            return servlet;
165        }
166    
167        public synchronized SslSocketConnector getSslSocketConnector() {
168            if (sslSocketConnector == null) {
169                sslSocketConnector = new SslSocketConnector();
170                // with default null values, jetty ssl system properties
171                // and console will be read by jetty implementation
172                sslSocketConnector.setPassword(sslPassword);
173                sslSocketConnector.setKeyPassword(sslKeyPassword);
174                if (sslKeystore != null) {
175                    sslSocketConnector.setKeystore(sslKeystore);
176                }
177            }
178            return sslSocketConnector;
179        }
180    
181        public Server getServer() throws Exception {
182            if (server == null) {
183                server = createServer();
184            }
185            return server;
186        }
187    
188        public void setServer(Server server) {
189            this.server = server;
190        }
191    
192        public String getSslKeyPassword() {
193            return sslKeyPassword;
194        }
195    
196        public String getSslPassword() {
197            return sslPassword;
198        }
199    
200        public String getSslKeystore() {
201            return sslKeystore;
202        }
203    
204        public void setSslKeyPassword(String sslKeyPassword) {
205            this.sslKeyPassword = sslKeyPassword;
206        }
207    
208        public void setSslPassword(String sslPassword) {
209            this.sslPassword = sslPassword;
210        }
211    
212        public void setSslKeystore(String sslKeystore) {
213            this.sslKeystore = sslKeystore;
214        }
215    
216        protected Server createServer() throws Exception {
217            Server server = new Server();
218            ContextHandlerCollection collection = new ContextHandlerCollection();
219            collection.setServer(server);
220            server.addHandler(collection);
221            server.start();
222    
223            return server;
224        }
225    
226        @Override
227        protected void doStop() throws Exception {
228            for (ConnectorRef connectorRef : connectors.values()) {
229                connectorRef.connector.stop();
230            }
231            connectors.clear();
232    
233            if (server != null) {
234                server.stop();
235            }
236            super.doStop();
237        }
238    
239        @Override
240        protected void doStart() throws Exception {
241            super.doStart();
242        }
243    
244    }