1 /******************************************************************************* 2 * Copyright (c) 2005, 2015 Cognos Incorporated, IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Cognos Incorporated - initial API and implementation 13 * IBM Corporation - bug fixes and enhancements 14 * Raymond Augé <raymond.auge@liferay.com> - Bug 436698 15 *******************************************************************************/ 16 17 package org.eclipse.equinox.http.servlet.internal; 18 19 import java.util.*; 20 import java.util.Map.Entry; 21 import java.util.concurrent.ConcurrentHashMap; 22 import java.util.concurrent.ConcurrentMap; 23 import javax.servlet.*; 24 import javax.servlet.http.HttpServlet; 25 import org.eclipse.equinox.http.servlet.ExtendedHttpService; 26 import org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet; 27 import org.eclipse.equinox.http.servlet.internal.util.*; 28 import org.osgi.framework.*; 29 import org.osgi.service.http.HttpService; 30 import org.osgi.service.http.runtime.HttpServiceRuntime; 31 import org.osgi.service.http.runtime.HttpServiceRuntimeConstants; 32 import org.osgi.util.tracker.ServiceTracker; 33 import org.osgi.util.tracker.ServiceTrackerCustomizer; 34 35 public class Activator 36 implements BundleActivator, ServiceTrackerCustomizer<HttpServlet, HttpTuple> { 37 38 private static final String DEFAULT_SERVICE_DESCRIPTION = "Equinox Servlet Bridge"; //$NON-NLS-1$ 39 private static final String DEFAULT_SERVICE_VENDOR = "Eclipse.org"; //$NON-NLS-1$ 40 private static final String PROP_GLOBAL_WHITEBOARD = "equinox.http.global.whiteboard"; //$NON-NLS-1$ 41 public static final String UNIQUE_SERVICE_ID = "equinox.http.id"; //$NON-NLS-1$ 42 private static final String[] HTTP_SERVICES_CLASSES = new String[] { 43 HttpService.class.getName(), ExtendedHttpService.class.getName() 44 }; 45 46 private static volatile BundleContext context; 47 private static ConcurrentMap<ProxyServlet, Object> registrations = 48 new ConcurrentHashMap<ProxyServlet, Object>(); 49 50 private ServiceTracker<HttpServlet, HttpTuple> serviceTracker; 51 addProxyServlet(ProxyServlet proxyServlet)52 public static void addProxyServlet(ProxyServlet proxyServlet) { 53 Object previousRegistration = registrations.putIfAbsent( 54 proxyServlet, proxyServlet); 55 BundleContext currentContext = context; 56 try { 57 if (!(previousRegistration instanceof ServiceRegistration) && 58 (currentContext != null)) { 59 ServiceRegistration<HttpServlet> serviceRegistration = 60 currentContext.registerService( 61 HttpServlet.class, proxyServlet, 62 new Hashtable<String, Object>()); 63 64 registrations.put(proxyServlet, serviceRegistration); 65 } 66 } catch (IllegalStateException ex) { 67 //If the currentContext is no longer valid. 68 return; 69 } 70 } 71 unregisterHttpService(ProxyServlet proxyServlet)72 public static void unregisterHttpService(ProxyServlet proxyServlet) { 73 Object registration = registrations.remove(proxyServlet); 74 75 if (registration instanceof ServiceRegistration) { 76 ServiceRegistration<?> serviceRegistration = 77 (ServiceRegistration<?>)registration; 78 79 serviceRegistration.unregister(); 80 } 81 } 82 start(BundleContext bundleContext)83 public void start(BundleContext bundleContext) throws Exception { 84 context = bundleContext; 85 86 processRegistrations(); 87 88 serviceTracker = new ServiceTracker<HttpServlet, HttpTuple>( 89 bundleContext, HttpServlet.class, this); 90 91 serviceTracker.open(); 92 } 93 stop(BundleContext bundleContext)94 public void stop(BundleContext bundleContext) throws Exception { 95 serviceTracker.close(); 96 serviceTracker = null; 97 context = null; 98 } 99 addingService( ServiceReference<HttpServlet> serviceReference)100 public HttpTuple addingService( 101 ServiceReference<HttpServlet> serviceReference) { 102 BundleContext currentContext = context; 103 if (currentContext == null) { 104 return null; 105 } 106 107 try { 108 HttpServlet httpServlet = currentContext.getService(serviceReference); 109 110 if (!(httpServlet instanceof ProxyServlet)) { 111 currentContext.ungetService(serviceReference); 112 return null; 113 } 114 115 ProxyServlet proxyServlet = (ProxyServlet)httpServlet; 116 117 ServletConfig servletConfig = proxyServlet.getServletConfig(); 118 ServletContext servletContext = servletConfig.getServletContext(); 119 120 Dictionary<String, Object> serviceProperties = 121 new Hashtable<String, Object>(3); 122 123 Enumeration<String> initparameterNames = 124 servletConfig.getInitParameterNames(); 125 126 while (initparameterNames.hasMoreElements()) { 127 String name = initparameterNames.nextElement(); 128 129 serviceProperties.put( 130 name, servletConfig.getInitParameter(name)); 131 } 132 133 if (serviceProperties.get(Constants.SERVICE_VENDOR) == null) { 134 serviceProperties.put( 135 Constants.SERVICE_VENDOR, DEFAULT_SERVICE_VENDOR); 136 } 137 138 if (serviceProperties.get(Constants.SERVICE_DESCRIPTION) == null) { 139 serviceProperties.put( 140 Constants.SERVICE_DESCRIPTION, DEFAULT_SERVICE_DESCRIPTION); 141 } 142 143 Object httpServiceEndpointObj = serviceProperties.get(HttpServiceRuntimeConstants.HTTP_SERVICE_ENDPOINT); 144 145 if (httpServiceEndpointObj == null) { 146 String[] httpServiceEndpoints = getHttpServiceEndpoints( 147 serviceProperties, servletContext, servletConfig.getServletName()); 148 149 serviceProperties.put( 150 HttpServiceRuntimeConstants.HTTP_SERVICE_ENDPOINT, 151 httpServiceEndpoints); 152 } 153 else { 154 List<String> httpServiceEndpoints = new ArrayList<String>(); 155 156 String contextPath = servletContext.getContextPath(); 157 158 for (String httpServiceEndpoint : StringPlus.from(httpServiceEndpointObj)) { 159 if (!httpServiceEndpoint.startsWith(Const.HTTP.concat(":")) && !httpServiceEndpoint.startsWith(contextPath)) { //$NON-NLS-1$ 160 httpServiceEndpoint = contextPath + httpServiceEndpoint; 161 } 162 163 httpServiceEndpoints.add(httpServiceEndpoint); 164 } 165 166 serviceProperties.put( 167 HttpServiceRuntimeConstants.HTTP_SERVICE_ENDPOINT, 168 httpServiceEndpoints); 169 } 170 171 // need a unique id for our service to match old HttpService HttpContext 172 serviceProperties.put(UNIQUE_SERVICE_ID, new Random().nextLong()); 173 // white board support 174 // determine if the system bundle context should be used: 175 boolean useSystemContext = Boolean.valueOf(currentContext.getProperty(PROP_GLOBAL_WHITEBOARD)); 176 BundleContext trackingContext = useSystemContext ? currentContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext() : currentContext; 177 HttpServiceRuntimeImpl httpServiceRuntime = new HttpServiceRuntimeImpl( 178 trackingContext, currentContext, servletContext, serviceProperties); 179 httpServiceRuntime.open(); 180 181 proxyServlet.setHttpServiceRuntimeImpl(httpServiceRuntime); 182 183 // imperative API support; 184 // the http service must be registered first so we can get its service id 185 HttpServiceFactory httpServiceFactory = new HttpServiceFactory(httpServiceRuntime); 186 ServiceRegistration<?> hsfRegistration = currentContext.registerService( 187 HTTP_SERVICES_CLASSES, httpServiceFactory, serviceProperties); 188 189 serviceProperties.put(HttpServiceRuntimeConstants.HTTP_SERVICE_ID, Collections.singletonList(hsfRegistration.getReference().getProperty(Constants.SERVICE_ID))); 190 191 ServiceRegistration<HttpServiceRuntime> hsrRegistration = 192 currentContext.registerService( 193 HttpServiceRuntime.class, httpServiceRuntime, 194 serviceProperties); 195 196 httpServiceRuntime.setHsrRegistration(hsrRegistration); 197 198 return new HttpTuple( 199 proxyServlet, httpServiceFactory, hsfRegistration, 200 httpServiceRuntime, hsrRegistration); 201 } catch (IllegalStateException ex) { 202 //If the currentContext is no longer valid. 203 return null; 204 } 205 } 206 modifiedService( ServiceReference<HttpServlet> serviceReference, HttpTuple httpTuple)207 public void modifiedService( 208 ServiceReference<HttpServlet> serviceReference, HttpTuple httpTuple) { 209 210 removedService(serviceReference, httpTuple); 211 addingService(serviceReference); 212 } 213 removedService( ServiceReference<HttpServlet> serviceReference, HttpTuple httpTuple)214 public void removedService( 215 ServiceReference<HttpServlet> serviceReference, HttpTuple httpTuple) { 216 BundleContext currentContext = context; 217 if (currentContext != null) { 218 try { 219 currentContext.ungetService(serviceReference); 220 httpTuple.destroy(); 221 } catch (IllegalStateException ex) { 222 //If the currentContext is no longer valid. 223 return; 224 } 225 } 226 } 227 getHttpServiceEndpoints( Dictionary<String, Object> serviceProperties, ServletContext servletContext, String servletName)228 private String[] getHttpServiceEndpoints( 229 Dictionary<String, Object> serviceProperties, ServletContext servletContext, String servletName) { 230 231 List<String> httpServiceEndpoints = new ArrayList<String>(); 232 233 String contextPath = (String)serviceProperties.get(Const.CONTEXT_PATH); 234 235 if ((contextPath != null)) { 236 String httpHost = (String)serviceProperties.get(Const.HTTP_HOST); 237 String httpPort = (String)serviceProperties.get(Const.HTTP_PORT); 238 239 if (httpPort != null) { 240 if (httpHost == null) { 241 String endpoint = assembleEndpoint(Const.HTTP, Const.LOCALHOST, httpPort, contextPath); 242 httpServiceEndpoints.add(endpoint); 243 } 244 else { 245 String endpoint = assembleEndpoint(Const.HTTP, httpHost, httpPort, contextPath); 246 httpServiceEndpoints.add(endpoint); 247 } 248 } 249 250 String httpsHost = (String)serviceProperties.get(Const.HTTPS_HOST); 251 String httpsPort = (String)serviceProperties.get(Const.HTTPS_PORT); 252 253 if (httpsPort != null) { 254 if (httpsHost == null) { 255 String endpoint = assembleEndpoint(Const.HTTPS, Const.LOCALHOST, httpsPort, contextPath); 256 httpServiceEndpoints.add(endpoint); 257 } 258 else { 259 String endpoint = assembleEndpoint(Const.HTTPS, httpHost, httpsPort, contextPath); 260 httpServiceEndpoints.add(endpoint); 261 } 262 } 263 264 if (!httpServiceEndpoints.isEmpty()) { 265 return httpServiceEndpoints.toArray(new String[0]); 266 } 267 } 268 269 contextPath = servletContext.getContextPath(); 270 271 ServletRegistration servletRegistration = null; 272 try { 273 servletRegistration = servletContext.getServletRegistration(servletName); 274 } catch (UnsupportedOperationException e) { 275 StringBuilder sb = new StringBuilder(); 276 sb.append("Could not find the servlet registration for the servlet: "); //$NON-NLS-1$ 277 sb.append(servletName); 278 sb.append(" The Http Service will not be able to locate it's root path."); //$NON-NLS-1$ 279 sb.append(" This can be overcome by specifying an init-param with name 'osgi.http.endpoint'"); //$NON-NLS-1$ 280 sb.append(" and value equal to the servlet mapping minus the glob character '*'."); //$NON-NLS-1$ 281 servletContext.log(sb.toString()); 282 } 283 284 if (servletRegistration == null) { 285 return new String[0]; 286 } 287 288 Collection<String> mappings = servletRegistration.getMappings(); 289 290 for (String mapping : mappings) { 291 if (mapping.indexOf('/') == 0) { 292 if (mapping.charAt(mapping.length() - 1) == '*') { 293 mapping = mapping.substring(0, mapping.length() - 1); 294 295 if ((mapping.length() > 1) && 296 (mapping.charAt(mapping.length() - 1) != '/')) { 297 298 mapping += '/'; 299 } 300 } 301 302 httpServiceEndpoints.add(contextPath + mapping); 303 } 304 } 305 306 return httpServiceEndpoints.toArray(new String[0]); 307 } 308 assembleEndpoint(String protocol, String host, String port, String contextPath)309 private String assembleEndpoint(String protocol, String host, String port, String contextPath) { 310 StringBuilder sb = new StringBuilder(); 311 sb.append(protocol); 312 sb.append(Const.PROTOCOL); 313 sb.append(host); 314 sb.append(':'); 315 sb.append(port); 316 sb.append(contextPath); 317 if (sb.charAt(sb.length() - 1) != '/') { 318 sb.append('/'); 319 } 320 return sb.toString(); 321 } 322 processRegistrations()323 private void processRegistrations() { 324 Iterator<Entry<ProxyServlet, Object>> iterator = 325 registrations.entrySet().iterator(); 326 BundleContext currentContext = context; 327 if (currentContext == null) { 328 return; 329 } 330 331 while (iterator.hasNext()) { 332 Entry<ProxyServlet, Object> entry = iterator.next(); 333 ProxyServlet proxyServlet = entry.getKey(); 334 Object value = entry.getValue(); 335 336 try { 337 if (!(value instanceof ServiceRegistration)) { 338 ServiceRegistration<HttpServlet> serviceRegistration = 339 currentContext.registerService( 340 HttpServlet.class, proxyServlet, 341 new Hashtable<String, Object>()); 342 343 entry.setValue(serviceRegistration); 344 } 345 } catch (IllegalStateException ex) { 346 //If the currentContext is no longer valid. 347 return; 348 } 349 } 350 } 351 352 }