Skip to content

Package: OSGiMainHandler

OSGiMainHandler

nameinstructionbranchcomplexitylinemethod
OSGiMainHandler(Logger, Bundle)
M: 20 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
findOrCreateOSGiServletHandler(Servlet, HttpContext, Dictionary)
M: 106 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 23 C: 0
0%
M: 1 C: 0
0%
getProcessingLock()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getRemovalLock()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
registerFilter(Filter, String, Dictionary, HttpContext, HttpService)
M: 55 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 14 C: 0
0%
M: 1 C: 0
0%
registerResourceHandler(String, HttpContext, String, HttpService)
M: 53 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%
registerServletHandler(String, Servlet, Dictionary, HttpContext, HttpService)
M: 42 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 14 C: 0
0%
M: 1 C: 0
0%
service(Request, Response)
M: 88 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 32 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
unregisterAlias(String)
M: 31 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
unregisterAll()
M: 32 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
unregisterFilter(Filter)
M: 24 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
updateMappingInfo(Request, String, String)
M: 46 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 12 C: 0
0%
M: 1 C: 0
0%
uregisterAllLocal()
M: 54 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
validateAlias4RegOk(String)
M: 52 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
validateServlet4RegOk(Servlet)
M: 16 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2009, 2020 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.glassfish.grizzly.osgi.httpservice;
18:
19: import java.util.ArrayList;
20: import java.util.Dictionary;
21: import java.util.Enumeration;
22: import java.util.HashMap;
23: import java.util.List;
24: import java.util.Set;
25: import java.util.TreeSet;
26: import java.util.concurrent.locks.ReentrantLock;
27: import java.util.concurrent.locks.ReentrantReadWriteLock;
28:
29: import org.glassfish.grizzly.http.server.HttpHandler;
30: import org.glassfish.grizzly.http.server.Request;
31: import org.glassfish.grizzly.http.server.Response;
32: import org.glassfish.grizzly.http.server.util.MappingData;
33: import org.glassfish.grizzly.osgi.httpservice.util.Logger;
34: import org.glassfish.grizzly.servlet.FilterRegistration;
35: import org.osgi.framework.Bundle;
36: import org.osgi.service.http.HttpContext;
37: import org.osgi.service.http.HttpService;
38: import org.osgi.service.http.NamespaceException;
39:
40: import jakarta.servlet.Filter;
41: import jakarta.servlet.Servlet;
42: import jakarta.servlet.ServletException;
43:
44: /**
45: * OSGi Main HttpHandler.
46: * <p/>
47: * Dispatching HttpHandler. Grizzly integration.
48: * <p/>
49: * Responsibilities:
50: * <ul>
51: * <li>Manages registration data.</li>
52: * <li>Dispatching {@link HttpHandler#service(Request, Response)} method call to registered {@link HttpHandler}s.</li>
53: * </ul>
54: *
55: * @author Hubert Iwaniuk
56: */
57: public class OSGiMainHandler extends HttpHandler implements OSGiHandler {
58: private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
59: private final Logger logger;
60: private final Bundle bundle;
61: private final OSGiCleanMapper mapper;
62:
63: /**
64: * Constructor.
65: *
66: * @param logger Logger utility.
67: * @param bundle Bundle that we create if for, for local data reference.
68: */
69: public OSGiMainHandler(Logger logger, Bundle bundle) {
70: this.logger = logger;
71: this.bundle = bundle;
72: this.mapper = new OSGiCleanMapper(logger);
73: }
74:
75: /**
76: * Service method dispatching to registered handlers.
77: * <p/>
78: * {@inheritDoc}
79: */
80: @Override
81: public void service(Request request, Response response) throws Exception {
82: boolean invoked = false;
83: String alias = request.getDecodedRequestURI();
84: String originalAlias = alias;
85: logger.debug("Serviceing URI: " + alias);
86: // first lookup needs to be done for full match.
87: boolean cutOff = false;
88: while (true) {
89: logger.debug("CutOff: " + cutOff + ", alias: " + alias);
90: alias = OSGiCleanMapper.map(alias, cutOff);
91:• if (alias == null) {
92:• if (cutOff) {
93: // not found
94: break;
95: } else {
96: // switching to reducing mapping mode (removing after last '/' and searching)
97: logger.debug("Swithcing to reducing mapping mode.");
98: cutOff = true;
99: alias = originalAlias;
100: }
101: } else {
102: HttpHandler httpHandler = OSGiCleanMapper.getHttpHandler(alias);
103:
104: ((OSGiHandler) httpHandler).getProcessingLock().lock();
105: try {
106: updateMappingInfo(request, alias, originalAlias);
107:
108: httpHandler.service(request, response);
109: } finally {
110: ((OSGiHandler) httpHandler).getProcessingLock().unlock();
111: }
112: invoked = true;
113:• if (response.getStatus() != 404) {
114: break;
115:• } else if ("/".equals(alias)) {
116: // 404 in "/", cutoff algo will not escape this one.
117: break;
118:• } else if (!cutOff) {
119: // not found and haven't run in cutoff mode
120: cutOff = true;
121: }
122: }
123: }
124:• if (!invoked) {
125: try {
126: response.sendError(404);
127: } catch (Exception e) {
128: logger.warn("Failed to commit 404 status.", e);
129: }
130: }
131: }
132:
133: /**
134: * Registers {@link org.glassfish.grizzly.osgi.httpservice.OSGiServletHandler} in OSGi Http Service.
135: * <p/>
136: * Keeps truck of all registrations, takes care of thread safety.
137: *
138: * @param alias Alias to register, if wrong value than throws {@link org.osgi.service.http.NamespaceException}.
139: * @param servlet Servlet to register under alias, if fails to
140: * {@link jakarta.servlet.Servlet#init(jakarta.servlet.ServletConfig)} throws {@link jakarta.servlet.ServletException}.
141: * @param initparams Initial parameters to populate {@link jakarta.servlet.ServletContext} with.
142: * @param context OSGi {@link org.osgi.service.http.HttpContext}, provides mime handling, security and bundle specific
143: * resource access.
144: * @param httpService Used to {@link HttpService#createDefaultHttpContext()} if needed.
145: * @throws org.osgi.service.http.NamespaceException If alias was invalid or already registered.
146: * @throws jakarta.servlet.ServletException If {@link jakarta.servlet.Servlet#init(jakarta.servlet.ServletConfig)}
147: * fails.
148: */
149: public void registerServletHandler(final String alias, final Servlet servlet, final Dictionary initparams, HttpContext context,
150: final HttpService httpService) throws NamespaceException, ServletException {
151:
152: ReentrantLock lock = OSGiCleanMapper.getLock();
153: lock.lock();
154: try {
155: validateAlias4RegOk(alias);
156: validateServlet4RegOk(servlet);
157:
158:• if (context == null) {
159: logger.debug("No HttpContext provided, creating default");
160: context = httpService.createDefaultHttpContext();
161: }
162:
163: OSGiServletHandler servletHandler = findOrCreateOSGiServletHandler(servlet, context, initparams);
164: servletHandler.setServletPath(alias);
165:
166: logger.debug("Initializing Servlet been registered");
167: servletHandler.startServlet(); // this might throw ServletException, throw it to offending bundle.
168:
169: mapper.addHttpHandler(alias, servletHandler);
170: } finally {
171: lock.unlock();
172: }
173: }
174:
175: /**
176: *
177: * @param filter
178: * @param urlPattern
179: * @param initparams
180: * @param context
181: * @param httpService
182: * @throws NamespaceException
183: * @throws ServletException
184: */
185: public void registerFilter(final Filter filter, final String urlPattern, final Dictionary initparams, HttpContext context, final HttpService httpService)
186: throws ServletException {
187:
188: ReentrantLock lock = OSGiCleanMapper.getLock();
189: lock.lock();
190: try {
191:
192:• if (context == null) {
193: logger.debug("No HttpContext provided, creating default");
194: context = httpService.createDefaultHttpContext();
195: }
196:
197: OSGiServletContext servletContext = mapper.getServletContext(context);
198:• if (servletContext == null) {
199: mapper.addContext(context, null);
200: servletContext = mapper.getServletContext(context);
201: }
202:
203: FilterRegistration registration = servletContext.addFilter(Integer.toString(filter.hashCode()), filter);
204: registration.addMappingForUrlPatterns(null, urlPattern);
205:
206: filter.init(new OSGiFilterConfig(servletContext));
207:
208: } finally {
209: lock.unlock();
210: }
211: }
212:
213: /**
214: * Registers {@link OSGiResourceHandler} in OSGi Http Service.
215: * <p/>
216: * Keeps truck of all registrations, takes care of thread safety.
217: *
218: * @param alias Alias to register, if wrong value than throws {@link NamespaceException}.
219: * @param context OSGi {@link HttpContext}, provides mime handling, security and bundle specific resource access.
220: * @param internalPrefix Prefix to map request for this alias to.
221: * @param httpService Used to {@link HttpService#createDefaultHttpContext()} if needed.
222: * @throws NamespaceException If alias was invalid or already registered.
223: */
224: public void registerResourceHandler(String alias, HttpContext context, String internalPrefix, HttpService httpService) throws NamespaceException {
225:
226: ReentrantLock lock = OSGiCleanMapper.getLock();
227: lock.lock();
228: try {
229: validateAlias4RegOk(alias);
230:
231:• if (context == null) {
232: logger.debug("No HttpContext provided, creating default");
233: context = httpService.createDefaultHttpContext();
234: }
235:• if (internalPrefix == null) {
236: internalPrefix = "";
237: }
238:
239: OSGiServletContext servletContext = mapper.getServletContext(context);
240:• if (servletContext == null) {
241: mapper.addContext(context, null);
242: servletContext = mapper.getServletContext(context);
243: }
244:
245: mapper.addHttpHandler(alias, new OSGiResourceHandler(alias, internalPrefix, context, servletContext, logger));
246: } finally {
247: lock.unlock();
248: }
249: }
250:
251: /**
252: * Unregisters previously registered alias.
253: * <p/>
254: * Keeps truck of all registrations, takes care of thread safety.
255: *
256: * @param alias Alias to unregister, if not owning alias {@link IllegalArgumentException} is thrown.
257: * @throws IllegalArgumentException If alias was not registered by calling bundle.
258: */
259: public void unregisterAlias(String alias) {
260:
261: ReentrantLock lock = OSGiCleanMapper.getLock();
262: lock.lock();
263: try {
264:• if (mapper.isLocalyRegisteredAlias(alias)) {
265: mapper.doUnregister(alias, true);
266: } else {
267: logger.warn("Bundle: " + bundle + " tried to unregister not owned alias '" + alias + '\'');
268: throw new IllegalArgumentException("Alias '" + alias + "' was not registered by you.");
269: }
270: } finally {
271: lock.unlock();
272: }
273: }
274:
275: public void unregisterFilter(final Filter filter) {
276: ReentrantLock lock = OSGiCleanMapper.getLock();
277: lock.lock();
278: try {
279:• for (OSGiServletContext servletContext : mapper.httpContextToServletContextMap.values()) {
280: servletContext.unregisterFilter(filter);
281: }
282: } finally {
283: lock.unlock();
284: }
285: }
286:
287: /**
288: * Unregisters all <code>alias</code>es registered by owning bundle.
289: */
290: public void uregisterAllLocal() {
291: logger.info("Unregistering all aliases registered by owning bundle");
292:
293: ReentrantLock lock = OSGiCleanMapper.getLock();
294: lock.lock();
295: try {
296:• for (String alias : mapper.getLocalAliases()) {
297: logger.debug("Unregistering '" + alias + "'");
298: // remember not to call Servlet.destroy() owning bundle might be stopped already.
299: mapper.doUnregister(alias, false);
300:• for (OSGiServletContext servletContext : mapper.httpContextToServletContextMap.values()) {
301: servletContext.unregisterAllFilters();
302: }
303: mapper.httpContextToServletContextMap.clear();
304: }
305: } finally {
306: lock.unlock();
307: }
308: }
309:
310: /**
311: * Part of Shutdown sequence. Unregister and clean up.
312: */
313: public void unregisterAll() {
314: logger.info("Unregistering all registered aliases");
315:
316: ReentrantLock lock = OSGiCleanMapper.getLock();
317: lock.lock();
318: try {
319: Set<String> aliases = OSGiCleanMapper.getAllAliases();
320:• while (!aliases.isEmpty()) {
321: String alias = ((TreeSet<String>) aliases).first();
322: logger.debug("Unregistering '" + alias + "'");
323: // remember not to call Servlet.destroy() owning bundle might be stopped already.
324: mapper.doUnregister(alias, false);
325: }
326: } finally {
327: lock.unlock();
328: }
329: }
330:
331: /**
332: * {@inheritDoc}
333: */
334: @Override
335: public ReentrantReadWriteLock.ReadLock getProcessingLock() {
336: return lock.readLock();
337: }
338:
339: /**
340: * {@inheritDoc}
341: */
342: @Override
343: public ReentrantReadWriteLock.WriteLock getRemovalLock() {
344: return lock.writeLock();
345: }
346:
347: /**
348: * Chek if <code>alias</code> has been already registered.
349: *
350: * @param alias Alias to check.
351: * @throws NamespaceException If <code>alias</code> has been registered.
352: */
353: private void validateAlias4RegOk(String alias) throws NamespaceException {
354:• if (!alias.startsWith("/")) {
355: // have to start with "/"
356: String msg = "Invalid alias '" + alias + "', have to start with '/'.";
357: logger.warn(msg);
358: throw new NamespaceException(msg);
359: }
360:• if (alias.length() > 1 && alias.endsWith("/")) {
361: // if longer than "/", should not end with "/"
362: String msg = "Alias '" + alias + "' can't and with '/' with exception to alias '/'.";
363: logger.warn(msg);
364: throw new NamespaceException(msg);
365: }
366:• if (OSGiCleanMapper.containsAlias(alias)) {
367: String msg = "Alias: '" + alias + "', already registered";
368: logger.warn(msg);
369: throw new NamespaceException(msg);
370: }
371: }
372:
373: /**
374: * Check if <code>servlet</code> has been already registered.
375: * <p/>
376: * An instance of {@link Servlet} can be registered only once, so in case of servlet been registered before will throw
377: * {@link ServletException} as specified in OSGi HttpService Spec.
378: *
379: * @param servlet {@link Servlet} to check if can be registered.
380: * @throws ServletException Iff <code>servlet</code> has been registered before.
381: */
382: private void validateServlet4RegOk(Servlet servlet) throws ServletException {
383:• if (OSGiCleanMapper.containsServlet(servlet)) {
384: String msg = "Servlet: '" + servlet + "', already registered.";
385: logger.warn(msg);
386: throw new ServletException(msg);
387: }
388: }
389:
390: /**
391: * Looks up {@link OSGiServletHandler}.
392: * <p/>
393: * If is already registered for <code>httpContext</code> than create new instance based on already registered. Else
394: * Create new one.
395: * <p/>
396: *
397: * @param servlet {@link Servlet} been registered.
398: * @param httpContext {@link HttpContext} used for registration.
399: * @param initparams Init parameters that will be visible in {@link jakarta.servlet.ServletContext}.
400: * @return Found or created {@link OSGiServletHandler}.
401: */
402: private OSGiServletHandler findOrCreateOSGiServletHandler(Servlet servlet, HttpContext httpContext, Dictionary initparams) {
403: OSGiServletHandler osgiServletHandler;
404:
405: List<OSGiServletHandler> servletHandlers = mapper.getContext(httpContext);
406:• if (servletHandlers != null) {
407: logger.debug("Reusing ServletHandler");
408: // new servlet handler for same configuration, different servlet and alias
409: osgiServletHandler = servletHandlers.get(0).newServletHandler(servlet);
410: servletHandlers.add(osgiServletHandler);
411: } else {
412: logger.debug("Creating new ServletHandler");
413: HashMap<String, String> params;
414:• if (initparams != null) {
415: params = new HashMap<>(initparams.size());
416: Enumeration names = initparams.keys();
417:• while (names.hasMoreElements()) {
418: String name = (String) names.nextElement();
419: params.put(name, (String) initparams.get(name));
420: }
421: } else {
422: params = new HashMap<>(0);
423: }
424:
425: servletHandlers = new ArrayList<>(1);
426: mapper.addContext(httpContext, mapper.getServletContext(httpContext), servletHandlers);
427:
428: final OSGiServletContext servletContext = mapper.getServletContext(httpContext);
429:
430:• assert servletContext != null;
431:
432: osgiServletHandler = new OSGiServletHandler(servlet, httpContext, servletContext, params, logger);
433: servletHandlers.add(osgiServletHandler);
434: osgiServletHandler.setFilterChainFactory(servletContext.getFilterChainFactory());
435: }
436:
437: return osgiServletHandler;
438: }
439:
440: private void updateMappingInfo(final Request request, final String alias, final String originalAlias) {
441:
442: final MappingData mappingData = request.obtainMappingData();
443: mappingData.contextPath.setString("");
444:• if (alias.equals("/")) {
445: mappingData.wrapperPath.setString("");
446: } else {
447: mappingData.wrapperPath.setString(alias);
448: }
449:
450:• if (alias.length() != originalAlias.length()) {
451: String pathInfo = originalAlias.substring(alias.length());
452:• if (pathInfo.charAt(0) != '/') {
453: pathInfo = "/" + pathInfo;
454: }
455:
456: mappingData.pathInfo.setString(pathInfo);
457: }
458:
459: updatePaths(request, mappingData);
460: }
461: }