Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 1 | /* |
Richard S. Hall | 435c20c | 2006-09-28 20:11:35 +0000 | [diff] [blame] | 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 9 | * |
Richard S. Hall | 435c20c | 2006-09-28 20:11:35 +0000 | [diff] [blame] | 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 11 | * |
Richard S. Hall | 435c20c | 2006-09-28 20:11:35 +0000 | [diff] [blame] | 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 18 | */ |
| 19 | package org.apache.felix.http.jetty; |
| 20 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 21 | |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 22 | import java.lang.reflect.Constructor; |
| 23 | |
| 24 | import org.mortbay.http.HashUserRealm; |
| 25 | import org.mortbay.http.HttpServer; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 26 | import org.mortbay.http.JsseListener; |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 27 | import org.mortbay.http.SocketListener; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 28 | import org.mortbay.jetty.servlet.OsgiServletHandler; |
| 29 | import org.mortbay.jetty.servlet.ServletHttpContext; |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 30 | import org.mortbay.util.Code; |
| 31 | import org.mortbay.util.InetAddrPort; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 32 | import org.osgi.framework.Bundle; |
| 33 | import org.osgi.framework.BundleActivator; |
| 34 | import org.osgi.framework.BundleContext; |
| 35 | import org.osgi.framework.BundleException; |
| 36 | import org.osgi.framework.ServiceFactory; |
| 37 | import org.osgi.framework.ServiceRegistration; |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 38 | import org.osgi.service.http.HttpService; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 39 | |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 40 | |
| 41 | /** |
| 42 | * Basic implementation of OSGi HTTP service 1.1. |
| 43 | * |
| 44 | * TODO: |
| 45 | * |
| 46 | * - fuller suite of testing and compatibility tests |
| 47 | * |
| 48 | * - only exposed params are those defined in the OSGi spec. Jetty is |
| 49 | * very tunable via params, some of which it may be useful to expose |
| 50 | * |
| 51 | * - no cacheing is performed on delivered resources. Although not part |
| 52 | * of the OSGi spec, it also isn't precluded and would enhance |
| 53 | * performance in a high usage environment. Jetty's ResourceHandler |
| 54 | * class could be a model for this. |
| 55 | * |
| 56 | * - scanning the Jetty ResourceHandler class it's clear that there are |
| 57 | * many other sophisticated areas to do with resource handling such |
| 58 | * as checking date and range fields in the http headers. It's not clear |
| 59 | * whether any of these play a part in the OSGi service - the spec |
| 60 | * just describes "returning the contents of the URL to the client" which |
| 61 | * doesn't state what other HTTP handling might be compliant or desirable |
| 62 | */ |
| 63 | public class Activator implements BundleActivator |
| 64 | { |
| 65 | protected static boolean debug = false; |
| 66 | |
| 67 | private BundleContext m_bundleContext = null; |
| 68 | private ServiceRegistration m_svcReg = null; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 69 | private HttpServiceFactory m_httpServ = null; |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 70 | private HttpServer m_server = null; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 71 | private OsgiServletHandler m_hdlr = null; |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 72 | |
| 73 | private int m_httpPort; |
| 74 | private int m_httpsPort; |
| 75 | |
| 76 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 77 | public void start( BundleContext bundleContext ) throws BundleException |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 78 | { |
| 79 | m_bundleContext = bundleContext; |
| 80 | |
| 81 | // org.mortbay.util.Loader needs this (used for JDK 1.4 log classes) |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 82 | Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() ); |
| 83 | |
| 84 | String optDebug = m_bundleContext.getProperty( "org.apache.felix.http.jetty.debug" ); |
| 85 | if ( optDebug != null && optDebug.toLowerCase().equals( "true" ) ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 86 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 87 | Code.setDebug( true ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 88 | debug = true; |
| 89 | } |
| 90 | |
| 91 | // get default HTTP and HTTPS ports as per the OSGi spec |
| 92 | try |
| 93 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 94 | m_httpPort = Integer.parseInt( m_bundleContext.getProperty( "org.osgi.service.http.port" ) ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 95 | } |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 96 | catch ( Exception e ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 97 | { |
| 98 | // maybe log a message saying using default? |
| 99 | m_httpPort = 80; |
| 100 | } |
| 101 | |
| 102 | try |
| 103 | { |
| 104 | // TODO: work out how/when we should use the HTTPS port |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 105 | m_httpsPort = Integer.parseInt( m_bundleContext.getProperty( "org.osgi.service.http.port.secure" ) ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 106 | } |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 107 | catch ( Exception e ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 108 | { |
| 109 | // maybe log a message saying using default? |
| 110 | m_httpsPort = 443; |
| 111 | } |
| 112 | |
| 113 | try |
| 114 | { |
| 115 | initializeJetty(); |
| 116 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 117 | } |
| 118 | catch ( Exception ex ) |
| 119 | { |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 120 | //TODO: maybe throw a bundle exception in here? |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 121 | System.out.println( "Http2: " + ex ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 122 | return; |
| 123 | } |
| 124 | |
| 125 | m_httpServ = new HttpServiceFactory(); |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 126 | m_svcReg = m_bundleContext.registerService( HttpService.class.getName(), m_httpServ, null ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 127 | } |
| 128 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 129 | |
| 130 | public void stop( BundleContext bundleContext ) throws BundleException |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 131 | { |
| 132 | //TODO: wonder if we need to closedown service factory ??? |
| 133 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 134 | if ( m_svcReg != null ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 135 | { |
| 136 | m_svcReg.unregister(); |
| 137 | } |
| 138 | |
| 139 | try |
| 140 | { |
| 141 | m_server.stop(); |
| 142 | } |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 143 | catch ( Exception e ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 144 | { |
| 145 | //TODO: log some form of error |
| 146 | } |
| 147 | } |
| 148 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 149 | |
| 150 | protected void initializeJetty() throws Exception |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 151 | { |
| 152 | //TODO: Maybe create a separate "JettyServer" object here? |
| 153 | // Realm |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 154 | HashUserRealm realm = new HashUserRealm( "OSGi HTTP Service Realm" ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 155 | |
| 156 | // Create server |
| 157 | m_server = new HttpServer(); |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 158 | m_server.addRealm( realm ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 159 | |
| 160 | // Add a regular HTTP listener |
| 161 | SocketListener listener = null; |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 162 | listener = ( SocketListener ) m_server.addListener( new InetAddrPort( m_httpPort ) ); |
| 163 | listener.setMaxIdleTimeMs( 60000 ); |
| 164 | |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 165 | // See if we need to add an HTTPS listener |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 166 | String enableHTTPS = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.enable" ); |
| 167 | if ( enableHTTPS != null && enableHTTPS.toLowerCase().equals( "true" ) ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 168 | { |
| 169 | initializeHTTPS(); |
| 170 | } |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 171 | |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 172 | m_server.start(); |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 173 | |
| 174 | // setup the Jetty web application context shared by all Http services |
| 175 | ServletHttpContext hdlrContext = new ServletHttpContext(); |
| 176 | hdlrContext.setContextPath( "/" ); |
| 177 | //TODO: was in original code, but seems we shouldn't serve |
| 178 | // resources in servlet context |
| 179 | //hdlrContext.setServingResources(true); |
| 180 | hdlrContext.setClassLoader( getClass().getClassLoader() ); |
| 181 | debug( " adding handler context : " + hdlrContext ); |
| 182 | m_server.addContext( hdlrContext ); |
| 183 | |
| 184 | m_hdlr = new OsgiServletHandler(); |
| 185 | hdlrContext.addHandler( m_hdlr ); |
| 186 | |
| 187 | try |
| 188 | { |
| 189 | hdlrContext.start(); |
| 190 | } |
| 191 | catch ( Exception e ) |
| 192 | { |
| 193 | // make sure we unwind the adding process |
| 194 | System.err.println( "Exception Starting Jetty Handler Context: " + e ); |
| 195 | e.printStackTrace( System.err ); |
| 196 | } |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 197 | } |
| 198 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 199 | |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 200 | //TODO: Just a basic implementation to give us a working HTTPS port. A better |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 201 | // long-term solution may be to separate out the SSL provider handling, |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 202 | // keystore, passwords etc. into it's own pluggable service |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 203 | protected void initializeHTTPS() throws Exception |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 204 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 205 | String sslProvider = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.provider" ); |
| 206 | if ( sslProvider == null ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 207 | { |
| 208 | sslProvider = "org.mortbay.http.SunJsseListener"; |
| 209 | } |
| 210 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 211 | // Set default jetty properties for supplied values. For any not set, |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 212 | // Jetty will fallback to checking system properties. |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 213 | String keystore = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.keystore" ); |
| 214 | if ( keystore != null ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 215 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 216 | System.setProperty( JsseListener.KEYSTORE_PROPERTY, keystore ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 217 | } |
| 218 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 219 | String passwd = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.password" ); |
| 220 | if ( passwd != null ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 221 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 222 | System.setProperty( JsseListener.PASSWORD_PROPERTY, passwd ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 223 | } |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 224 | |
| 225 | String keyPasswd = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.key.password" ); |
| 226 | if ( keyPasswd != null ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 227 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 228 | System.setProperty( JsseListener.KEYPASSWORD_PROPERTY, keyPasswd ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | //SunJsseListener s_listener = new SunJsseListener(new InetAddrPort(m_httpsPort)); |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 232 | Object args[] = |
| 233 | { new InetAddrPort( m_httpsPort ) }; |
| 234 | Class argTypes[] = |
| 235 | { args[0].getClass() }; |
| 236 | Class clazz = Class.forName( sslProvider ); |
| 237 | Constructor cstruct = clazz.getDeclaredConstructor( argTypes ); |
| 238 | JsseListener s_listener = ( JsseListener ) cstruct.newInstance( args ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 239 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 240 | m_server.addListener( s_listener ); |
| 241 | s_listener.setMaxIdleTimeMs( 60000 ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 242 | } |
| 243 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 244 | |
| 245 | protected static void debug( String txt ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 246 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 247 | if ( debug ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 248 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 249 | System.err.println( ">>Oscar HTTP: " + txt ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 250 | } |
| 251 | } |
| 252 | |
| 253 | // Inner class to provide basic service factory functionality |
| 254 | |
| 255 | public class HttpServiceFactory implements ServiceFactory |
| 256 | { |
| 257 | public HttpServiceFactory() |
| 258 | { |
| 259 | // Initialize the statics for the service implementation. |
| 260 | HttpServiceImpl.initializeStatics(); |
| 261 | } |
| 262 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 263 | |
| 264 | public Object getService( Bundle bundle, ServiceRegistration registration ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 265 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 266 | Object srv = new HttpServiceImpl( bundle, m_server, m_hdlr ); |
| 267 | debug( "** http service get:" + bundle + ", service: " + srv ); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 268 | return srv; |
| 269 | } |
| 270 | |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 271 | |
| 272 | public void ungetService( Bundle bundle, ServiceRegistration registration, Object service ) |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 273 | { |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 274 | debug( "** http service unget:" + bundle + ", service: " + service ); |
| 275 | ( ( HttpServiceImpl ) service ).unregisterAll(); |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 276 | } |
| 277 | } |
Felix Meschberger | b76cfdb | 2007-09-28 14:13:22 +0000 | [diff] [blame^] | 278 | |
Richard S. Hall | fe8e560 | 2006-04-19 15:23:22 +0000 | [diff] [blame] | 279 | } |