1 /*******************************************************************************
2  * Copyright (c) 2005, 2016 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  *     Juan Gonzalez <juan.gonzalez@liferay.com> - Bug 486412
16  *******************************************************************************/
17 package org.eclipse.equinox.http.servlet.internal.servlet;
18 
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.lang.reflect.*;
22 import java.net.URL;
23 import java.security.*;
24 import java.util.*;
25 import javax.servlet.*;
26 import org.eclipse.equinox.http.servlet.internal.context.*;
27 import org.eclipse.equinox.http.servlet.internal.util.Const;
28 import org.eclipse.equinox.http.servlet.internal.util.EventListeners;
29 import org.osgi.framework.Bundle;
30 import org.osgi.framework.wiring.BundleWiring;
31 import org.osgi.service.http.context.ServletContextHelper;
32 
33 public class ServletContextAdaptor {
34 
35 	private final static Map<Method, Method> contextToHandlerMethods;
36 
37 	static {
38 		contextToHandlerMethods = createContextToHandlerMethods();
39 	}
40 
createContextToHandlerMethods()41 	private static Map<Method, Method> createContextToHandlerMethods() {
42 		Map<Method, Method> methods = new HashMap<Method, Method>();
43 		Method[] handlerMethods =
44 			ServletContextAdaptor.class.getDeclaredMethods();
45 
46 		for (Method handlerMethod : handlerMethods) {
47 			String name = handlerMethod.getName();
48 			Class<?>[] parameterTypes = handlerMethod.getParameterTypes();
49 
50 			try {
51 				Method method = ServletContext.class.getMethod(
52 					name, parameterTypes);
53 				methods.put(method, handlerMethod);
54 			}
55 			catch (NoSuchMethodException e) {
56 				// do nothing
57 			}
58 		}
59 
60 		try {
61 			Method equalsMethod = Object.class.getMethod("equals", Object.class);  //$NON-NLS-1$
62 			Method equalsHandlerMethod = ServletContextAdaptor.class.getMethod("equals", Object.class); //$NON-NLS-1$
63 			methods.put(equalsMethod, equalsHandlerMethod);
64 
65 			Method hashCodeMethod = Object.class.getMethod("hashCode", (Class<?>[])null);  //$NON-NLS-1$
66 			Method hashCodeHandlerMethod = ServletContextAdaptor.class.getMethod("hashCode", (Class<?>[])null); //$NON-NLS-1$
67 			methods.put(hashCodeMethod, hashCodeHandlerMethod);
68 		}
69 		catch (NoSuchMethodException e) {
70 				// do nothing
71 		}
72 
73 		return methods;
74 	}
75 
ServletContextAdaptor( ContextController contextController, Bundle bundle, ServletContextHelper servletContextHelper, EventListeners eventListeners, AccessControlContext acc)76 	public ServletContextAdaptor(
77 		ContextController contextController, Bundle bundle,
78 		ServletContextHelper servletContextHelper,
79 		EventListeners eventListeners, AccessControlContext acc) {
80 
81 		this.contextController = contextController;
82 		this.proxyContext = contextController.getProxyContext();
83 		this.servletContext = proxyContext.getServletContext();
84 		this.servletContextHelper = servletContextHelper;
85 		this.eventListeners = eventListeners;
86 		this.acc = acc;
87 		this.bundle = bundle;
88 
89 		BundleWiring bundleWiring = this.bundle.adapt(BundleWiring.class);
90 
91 		this.classLoader = bundleWiring.getClassLoader();
92 	}
93 
createServletContext()94 	public ServletContext createServletContext() {
95 		Class<?> clazz = getClass();
96 		ClassLoader curClassLoader = clazz.getClassLoader();
97 		Class<?>[] interfaces = new Class[] {ServletContext.class};
98 
99 		return (ServletContext)Proxy.newProxyInstance(
100 			curClassLoader, interfaces, new AdaptorInvocationHandler());
101 	}
102 
103 	@Override
equals(Object obj)104 	public boolean equals (Object obj) {
105 		if (!(obj instanceof ServletContext)) {
106 			return false;
107 		}
108 
109 		if (!(Proxy.isProxyClass(obj.getClass())))  {
110 			return false;
111 		}
112 
113 		InvocationHandler invocationHandler = Proxy.getInvocationHandler(obj);
114 
115 		if (!(invocationHandler instanceof AdaptorInvocationHandler)) {
116 			return false;
117 		}
118 
119 		AdaptorInvocationHandler adaptorInvocationHandler = (AdaptorInvocationHandler)invocationHandler;
120 
121 		return contextController.equals(adaptorInvocationHandler.getContextController());
122 	}
123 
getClassLoader()124 	public ClassLoader getClassLoader() {
125 		return classLoader;
126 	}
127 
getContextPath()128 	public String getContextPath() {
129 		return contextController.getFullContextPath();
130 	}
131 
getAttribute(String attributeName)132 	public Object getAttribute(String attributeName) {
133 		Dictionary<String, Object> attributes = getContextAttributes();
134 
135 		if (attributeName.equals("osgi-bundlecontext")) { //$NON-NLS-1$
136 			return bundle.getBundleContext();
137 		}
138 
139 		return attributes.get(attributeName);
140 	}
141 
getAttributeNames()142 	public Enumeration<String> getAttributeNames() {
143 		Dictionary<String, Object> attributes = getContextAttributes();
144 		return attributes.keys();
145 	}
146 
getInitParameter(String name)147 	public String getInitParameter(String name) {
148 		return contextController.getInitParams().get(name);
149 	}
150 
getInitParameterNames()151 	public Enumeration<String> getInitParameterNames() {
152 		return Collections.enumeration(
153 			contextController.getInitParams().keySet());
154 	}
155 
getMimeType(final String name)156 	public String getMimeType(final String name) {
157 		String mimeType = null;
158 
159 		try {
160 			mimeType = AccessController.doPrivileged(
161 				new PrivilegedExceptionAction<String>() {
162 					@Override
163 					public String run() throws Exception {
164 						return servletContextHelper.getMimeType(name);
165 					}
166 				}, acc
167 			);
168 		}
169 		catch (PrivilegedActionException e) {
170 			servletContext.log(e.getException().getMessage(), e.getException());
171 		}
172 
173 		return (mimeType != null) ? mimeType : servletContext.getMimeType(name);
174 	}
175 
getNamedDispatcher(String servletName)176 	public RequestDispatcher getNamedDispatcher(String servletName) {
177 		DispatchTargets dispatchTargets = contextController.getDispatchTargets(
178 			servletName, null, null, null, null, null, Match.EXACT, null);
179 
180 		if (dispatchTargets == null) {
181 			return null;
182 		}
183 
184 		return new RequestDispatcherAdaptor(dispatchTargets, servletName);
185 	}
186 
getRealPath(final String path)187 	public String getRealPath(final String path) {
188 		try {
189 			return AccessController.doPrivileged(
190 				new PrivilegedExceptionAction<String>() {
191 					@Override
192 					public String run() throws Exception {
193 						return servletContextHelper.getRealPath(path);
194 					}
195 				}, acc
196 			);
197 		}
198 		catch (PrivilegedActionException e) {
199 			servletContext.log(e.getException().getMessage(), e.getException());
200 		}
201 
202 		return null;
203 	}
204 
205 	public RequestDispatcher getRequestDispatcher(String path) {
206 		// no relative paths supported, must begin with '/'
207 		if (!path.startsWith(Const.SLASH)) {
208 			return null;
209 		}
210 		// if the path starts with the full context path strip it
211 		if (path.startsWith(contextController.getFullContextPath())) {
212 			path = path.substring(contextController.getFullContextPath().length());
213 		}
214 
215 		DispatchTargets dispatchTargets = contextController.getDispatchTargets(path, null);
216 
217 		if (dispatchTargets == null) {
218 			return null;
219 		}
220 
221 		return new RequestDispatcherAdaptor(dispatchTargets, path);
222 	}
223 
224 	public URL getResource(final String name) {
225 		try {
226 			return AccessController.doPrivileged(
227 				new PrivilegedExceptionAction<URL>() {
228 					@Override
229 					public URL run() throws Exception {
230 						return servletContextHelper.getResource(name);
231 					}
232 				}, acc
233 			);
234 		}
235 		catch (PrivilegedActionException e) {
236 			servletContext.log(e.getException().getMessage(), e.getException());
237 		}
238 
239 		return null;
240 	}
241 
242 	public InputStream getResourceAsStream(String name) {
243 		URL url = getResource(name);
244 
245 		if (url != null) {
246 			try {
247 				return url.openStream();
248 			}
249 			catch (IOException ioe) {
250 				servletContext.log(ioe.getMessage(), ioe);
251 			}
252 		}
253 
254 		return null;
255 	}
256 
257 	/**
258 	 * 	@see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
259 	 */
260 	public Set<String> getResourcePaths(final String name) {
261 		if (name == null || !name.startsWith(Const.SLASH))
262 			return null;
263 
264 		try {
265 			return AccessController.doPrivileged(
266 				new PrivilegedExceptionAction<Set<String>>() {
267 					@Override
268 					public Set<String> run() throws Exception {
269 						return servletContextHelper.getResourcePaths(name);
270 					}
271 				}, acc
272 			);
273 		}
274 		catch (PrivilegedActionException e) {
275 			servletContext.log(e.getException().getMessage(), e.getException());
276 		}
277 
278 		return null;
279 	}
280 
281 	public String getServletContextName() {
282 		return contextController.getContextName();
283 	}
284 
285 	@Override
286 	public int hashCode() {
287 		return contextController.hashCode();
288 	}
289 
290 	public void removeAttribute(String attributeName) {
291 		Dictionary<String, Object> attributes = getContextAttributes();
292 		Object attributeValue = attributes.remove(attributeName);
293 
294 		List<ServletContextAttributeListener> listeners =
295 			eventListeners.get(ServletContextAttributeListener.class);
296 
297 		if (listeners.isEmpty()) {
298 			return;
299 		}
300 
301 		ServletContextAttributeEvent servletContextAttributeEvent =
302 			new ServletContextAttributeEvent(
303 				servletContextTL.get(), attributeName, attributeValue);
304 
305 		for (ServletContextAttributeListener servletContextAttributeListener : listeners) {
306 			servletContextAttributeListener.attributeRemoved(
307 				servletContextAttributeEvent);
308 		}
309 	}
310 
311 	public void addFilter(String arg1, Class<? extends Filter> arg2) {
312 		throw new UnsupportedOperationException();
313 	}
314 	public void addFilter(String arg1, String arg2) {
315 		throw new UnsupportedOperationException();
316 	}
317 	public void addFilter(String arg1, Filter arg2) {
318 		throw new UnsupportedOperationException();
319 	}
320 
321 	public void addListener(Class<?> arg1){
322 		throw new UnsupportedOperationException();
323 	}
324 	public void addListener(String arg1){
325 		throw new UnsupportedOperationException();
326 	}
327 	public void addListener(EventListener arg1){
328 		throw new UnsupportedOperationException();
329 	}
330 
331 	public void addServlet(String arg1, Class<? extends Servlet> arg2) {
332 		throw new UnsupportedOperationException();
333 	}
334 	public void addServlet(String arg1, String arg2) {
335 		throw new UnsupportedOperationException();
336 	}
337 	public void addServlet(String arg1, Servlet arg2) {
338 		throw new UnsupportedOperationException();
339 	}
340 
341 	public void createFilter(Class<?> arg1) {
342 		throw new UnsupportedOperationException();
343 	}
344 	public void createServlet(Class<?> arg1) {
345 		throw new UnsupportedOperationException();
346 	}
347 	public void createListener(Class<?> arg1) {
348 		throw new UnsupportedOperationException();
349 	}
350 
351 	public void declareRoles(String... arg1) {
352 		throw new UnsupportedOperationException();
353 	}
354 
355 	public void setAttribute(String attributeName, Object attributeValue) {
356 		if (attributeValue == null) {
357 			removeAttribute(attributeName);
358 
359 			return;
360 		}
361 
362 		Dictionary<String, Object> attributes = getContextAttributes();
363 		boolean added = (attributes.get(attributeName) == null);
364 		attributes.put(attributeName, attributeValue);
365 
366 		List<ServletContextAttributeListener> listeners =
367 			eventListeners.get(ServletContextAttributeListener.class);
368 
369 		if (listeners.isEmpty()) {
370 			return;
371 		}
372 
373 		ServletContextAttributeEvent servletContextAttributeEvent =
374 			new ServletContextAttributeEvent(
375 				servletContextTL.get(), attributeName, attributeValue);
376 
377 		for (ServletContextAttributeListener servletContextAttributeListener : listeners) {
378 			if (added) {
379 				servletContextAttributeListener.attributeAdded(
380 					servletContextAttributeEvent);
381 			}
382 			else {
383 				servletContextAttributeListener.attributeReplaced(
384 					servletContextAttributeEvent);
385 			}
386 		}
387 	}
388 
389 	@Override
390 	public String toString() {
391 		String value = string;
392 
393 		if (value == null) {
394 			value = SIMPLE_NAME + '[' + contextController + ']';
395 
396 			string = value;
397 		}
398 
399 		return value;
400 	}
401 
402 	Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
403 		boolean useThreadLocal =
404 			"removeAttribute".equals(method.getName()) || //$NON-NLS-1$
405 			"setAttribute".equals(method.getName()); //$NON-NLS-1$
406 
407 		if (useThreadLocal) {
408 			servletContextTL.set((ServletContext)proxy);
409 		}
410 
411 		try {
412 			Method m = contextToHandlerMethods.get(method);
413 			try {
414 				if (m != null) {
415 					return m.invoke(this, args);
416 				}
417 				return method.invoke(servletContext, args);
418 			} catch (InvocationTargetException e) {
419 				throw e.getCause();
420 			}
421 		}
422 		finally {
423 			if (useThreadLocal) {
424 				servletContextTL.remove();
425 			}
426 		}
427 	}
428 
429 	private Dictionary<String, Object> getContextAttributes() {
430 		return proxyContext.getContextAttributes(contextController);
431 	}
432 
433 	private class AdaptorInvocationHandler implements InvocationHandler {
434 		public AdaptorInvocationHandler() {}
435 
436 		public ContextController getContextController() {
437 			return contextController;
438 		}
439 
440 		@Override
441 		public Object invoke(Object proxy, Method method, Object[] args)
442 			throws Throwable {
443 
444 			return ServletContextAdaptor.this.invoke(proxy, method, args);
445 		}
446 
447 	}
448 
449 	private final static String SIMPLE_NAME =
450 		ServletContextAdaptor.class.getSimpleName();
451 
452 	private final static ThreadLocal<ServletContext> servletContextTL = new ThreadLocal<ServletContext>();
453 
454 	private final AccessControlContext acc;
455 	private final Bundle bundle;
456 	private final ClassLoader classLoader;
457 	final ContextController contextController;
458 	private final EventListeners eventListeners;
459 	private final ProxyContext proxyContext;
460 	private final ServletContext servletContext;
461 	final ServletContextHelper servletContextHelper;
462 	private String string;
463 
464 }
465