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.net.URL;
020 import java.util.ArrayList;
021 import java.util.LinkedHashMap;
022 import java.util.List;
023 import java.util.Map;
024
025 import org.apache.camel.Endpoint;
026 import org.apache.camel.impl.DefaultComponent;
027 import org.cometd.bayeux.server.BayeuxServer;
028 import org.cometd.bayeux.server.SecurityPolicy;
029 import org.cometd.server.BayeuxServerImpl;
030 import org.cometd.server.CometdServlet;
031 import org.eclipse.jetty.server.Connector;
032 import org.eclipse.jetty.server.Server;
033 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
034 import org.eclipse.jetty.server.nio.SelectChannelConnector;
035 import org.eclipse.jetty.server.session.HashSessionManager;
036 import org.eclipse.jetty.server.session.SessionHandler;
037 import org.eclipse.jetty.server.ssl.SslSocketConnector;
038 import org.eclipse.jetty.servlet.ServletContextHandler;
039 import org.eclipse.jetty.servlet.ServletHolder;
040 import org.eclipse.jetty.util.resource.Resource;
041 import org.slf4j.Logger;
042 import org.slf4j.LoggerFactory;
043
044
045 /**
046 * Component for Jetty Cometd
047 */
048 public class CometdComponent extends DefaultComponent {
049 private static final transient Logger LOG = LoggerFactory.getLogger(CometdComponent.class);
050
051 private final Map<String, ConnectorRef> connectors = new LinkedHashMap<String, ConnectorRef>();
052
053 private String sslKeyPassword;
054 private String sslPassword;
055 private String sslKeystore;
056 private SslSocketConnector sslSocketConnector;
057 private SecurityPolicy securityPolicy;
058 private List<BayeuxServer.Extension> extensions;
059
060 class ConnectorRef {
061 Connector connector;
062 CometdServlet servlet;
063 Server server;
064 int refCount;
065
066 public ConnectorRef(Connector connector, CometdServlet servlet, Server server) {
067 this.connector = connector;
068 this.servlet = servlet;
069 this.server = server;
070 increment();
071 }
072
073 public int increment() {
074 return ++refCount;
075 }
076
077 public int decrement() {
078 return --refCount;
079 }
080 }
081
082 public CometdComponent() {
083 }
084
085 @Override
086 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
087 setProperties(this, parameters);
088 return new CometdEndpoint(this, uri, remaining, parameters);
089 }
090
091 /**
092 * Connects the URL specified on the endpoint to the specified processor.
093 */
094 public void connect(CometdProducerConsumer prodcon) throws Exception {
095 // Make sure that there is a connector for the requested endpoint.
096 CometdEndpoint endpoint = (CometdEndpoint) prodcon.getEndpoint();
097 String connectorKey = endpoint.getProtocol() + ":" + endpoint.getUri().getHost() + ":" + endpoint.getPort();
098
099 synchronized (connectors) {
100 ConnectorRef connectorRef = connectors.get(connectorKey);
101 if (connectorRef == null) {
102 Connector connector;
103 if ("cometds".equals(endpoint.getProtocol())) {
104 connector = getSslSocketConnector();
105 } else {
106 connector = new SelectChannelConnector();
107 }
108 connector.setPort(endpoint.getPort());
109 connector.setHost(endpoint.getUri().getHost());
110 if ("localhost".equalsIgnoreCase(endpoint.getUri().getHost())) {
111 LOG.warn("You use localhost interface! It means that no external connections will be available."
112 + " Don't you want to use 0.0.0.0 instead (all network interfaces)?");
113 }
114 Server server = createServer();
115 server.addConnector(connector);
116
117 CometdServlet servlet = createServletForConnector(server, connector, endpoint);
118 connectorRef = new ConnectorRef(connector, servlet, server);
119 server.start();
120
121 connectors.put(connectorKey, connectorRef);
122 } else {
123 connectorRef.increment();
124 }
125
126 BayeuxServerImpl bayeux = connectorRef.servlet.getBayeux();
127
128 if (securityPolicy != null) {
129 bayeux.setSecurityPolicy(securityPolicy);
130 }
131 if (extensions != null) {
132 for (BayeuxServer.Extension extension : extensions) {
133 bayeux.addExtension(extension);
134 }
135 }
136 prodcon.setBayeux(bayeux);
137 }
138 }
139
140 /**
141 * Disconnects the URL specified on the endpoint from the specified
142 * processor.
143 */
144 public void disconnect(CometdProducerConsumer prodcon) throws Exception {
145 CometdEndpoint endpoint = prodcon.getEndpoint();
146
147 String connectorKey = endpoint.getProtocol() + ":" + endpoint.getUri().getHost() + ":" + endpoint.getPort();
148
149 synchronized (connectors) {
150 ConnectorRef connectorRef = connectors.get(connectorKey);
151 if (connectorRef != null) {
152 if (connectorRef.decrement() == 0) {
153 connectorRef.server.removeConnector(connectorRef.connector);
154 connectorRef.connector.stop();
155 connectorRef.server.stop();
156 connectors.remove(connectorKey);
157 }
158 }
159 }
160 }
161
162 protected CometdServlet createServletForConnector(Server server, Connector connector, CometdEndpoint endpoint) throws Exception {
163 CometdServlet servlet = new CometdServlet();
164
165 ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.NO_SECURITY | ServletContextHandler.NO_SESSIONS);
166 context.setConnectorNames(new String[]{connector.getName()});
167
168 ServletHolder holder = new ServletHolder();
169 holder.setServlet(servlet);
170 holder.setAsyncSupported(true);
171
172 // Use baseResource to pass as a parameter the url
173 // pointing to by example classpath:webapp
174 if (endpoint.getBaseResource() != null) {
175 String[] resources = endpoint.getBaseResource().split(":");
176 if (LOG.isDebugEnabled()) {
177 LOG.debug(">>> Protocol found: " + resources[0] + ", and resource: " + resources[1]);
178 }
179
180 if (resources[0].equals("file")) {
181 context.setBaseResource(Resource.newResource(resources[1]));
182 } else if (resources[0].equals("classpath")) {
183 // Create a URL handler using classpath protocol
184 URL url = this.getCamelContext().getClassResolver().loadResourceAsURL(resources[1]);
185 context.setBaseResource(Resource.newResource(url));
186 }
187 }
188
189 context.addServlet(holder, "/cometd/*");
190 context.addServlet("org.eclipse.jetty.servlet.DefaultServlet", "/");
191 context.setSessionHandler(new SessionHandler(new HashSessionManager()));
192
193 holder.setInitParameter("timeout", Integer.toString(endpoint.getTimeout()));
194 holder.setInitParameter("interval", Integer.toString(endpoint.getInterval()));
195 holder.setInitParameter("maxInterval", Integer.toString(endpoint.getMaxInterval()));
196 holder.setInitParameter("multiFrameInterval", Integer.toString(endpoint.getMultiFrameInterval()));
197 holder.setInitParameter("JSONCommented", Boolean.toString(endpoint.isJsonCommented()));
198 holder.setInitParameter("logLevel", Integer.toString(endpoint.getLogLevel()));
199
200 return servlet;
201 }
202
203 public synchronized SslSocketConnector getSslSocketConnector() {
204 if (sslSocketConnector == null) {
205 sslSocketConnector = new SslSocketConnector();
206 // with default null values, jetty ssl system properties
207 // and console will be read by jetty implementation
208 sslSocketConnector.getSslContextFactory().setKeyManagerPassword(sslPassword);
209 sslSocketConnector.getSslContextFactory().setKeyStorePassword(sslKeyPassword);
210 if (sslKeystore != null) {
211 sslSocketConnector.getSslContextFactory().setKeyStore(sslKeystore);
212 }
213 }
214 return sslSocketConnector;
215 }
216
217 public String getSslKeyPassword() {
218 return sslKeyPassword;
219 }
220
221 public String getSslPassword() {
222 return sslPassword;
223 }
224
225 public String getSslKeystore() {
226 return sslKeystore;
227 }
228
229 public void setSslKeyPassword(String sslKeyPassword) {
230 this.sslKeyPassword = sslKeyPassword;
231 }
232
233 public void setSslPassword(String sslPassword) {
234 this.sslPassword = sslPassword;
235 }
236
237 public void setSslKeystore(String sslKeystore) {
238 this.sslKeystore = sslKeystore;
239 }
240
241 public void setSecurityPolicy(SecurityPolicy securityPolicy) {
242 this.securityPolicy = securityPolicy;
243 }
244
245 public SecurityPolicy getSecurityPolicy() {
246 return securityPolicy;
247 }
248
249 public List<BayeuxServer.Extension> getExtensions() {
250 return extensions;
251 }
252
253 public void setExtensions(List<BayeuxServer.Extension> extensions) {
254 this.extensions = extensions;
255 }
256
257 public void addExtension(BayeuxServer.Extension extension) {
258 if (extensions == null) {
259 extensions = new ArrayList<BayeuxServer.Extension>();
260 }
261 extensions.add(extension);
262 }
263
264 protected Server createServer() throws Exception {
265 Server server = new Server();
266 ContextHandlerCollection collection = new ContextHandlerCollection();
267 server.setHandler(collection);
268 return server;
269 }
270
271 @Override
272 protected void doStop() throws Exception {
273 for (ConnectorRef connectorRef : connectors.values()) {
274 connectorRef.connector.stop();
275 }
276 connectors.clear();
277
278 super.doStop();
279 }
280
281 @Override
282 protected void doStart() throws Exception {
283 super.doStart();
284 }
285 }