| /* |
| * Copyright (c) 2020 Kentyou. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Kentyou - initial API and implementation |
| */ |
| package org.eclipse.sensinact.gateway.nthbnd.rest.osgi; |
| |
| import java.io.IOException; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.ServiceLoader; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import javax.servlet.Filter; |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| |
| import org.eclipse.jetty.websocket.servlet.WebSocketServlet; |
| import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; |
| import org.eclipse.sensinact.gateway.common.bundle.AbstractActivator; |
| import org.eclipse.sensinact.gateway.nthbnd.endpoint.NorthboundMediator; |
| import org.eclipse.sensinact.gateway.nthbnd.rest.internal.RestAccessConstants; |
| import org.eclipse.sensinact.gateway.nthbnd.rest.internal.http.CorsFilter; |
| import org.eclipse.sensinact.gateway.nthbnd.rest.internal.http.HttpEndpoint; |
| import org.eclipse.sensinact.gateway.nthbnd.rest.internal.http.HttpLoginEndpoint; |
| import org.eclipse.sensinact.gateway.nthbnd.rest.internal.http.HttpRegisteringEndpoint; |
| import org.eclipse.sensinact.gateway.nthbnd.rest.internal.ws.WebSocketConnectionFactory; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.wiring.BundleWiring; |
| import org.osgi.service.http.context.ServletContextHelper; |
| import org.osgi.service.http.whiteboard.HttpWhiteboardConstants; |
| |
| /** |
| * @see AbstractActivator |
| */ |
| public class Activator extends AbstractActivator<NorthboundMediator> { |
| |
| private CorsFilter corsFilter = null; |
| private boolean corsHeader = false; |
| |
| private static ClassLoader loader = null; |
| |
| private static void findJettyClassLoader(BundleContext context) { |
| Bundle[] bundles = context.getBundles(); |
| for(Bundle bundle:bundles) { |
| if("org.apache.felix.http.jetty".equals(bundle.getSymbolicName())) { |
| try { |
| BundleWiring wire = bundle.adapt(BundleWiring.class); |
| loader = wire.getClassLoader(); |
| }catch(Exception e) { |
| e.printStackTrace(); |
| loader = WebSocketServlet.class.getClassLoader(); |
| } |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator# |
| * doStart() |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public void doStart() throws Exception { |
| |
| findJettyClassLoader(super.mediator.getContext()); |
| |
| this.corsHeader = Boolean.valueOf((String) super.mediator.getProperty(RestAccessConstants.CORS_HEADER)); |
| if (this.corsHeader) { |
| this.corsFilter = new CorsFilter(mediator); |
| mediator.register(Activator.this.corsFilter, Filter.class, new Hashtable() {{ |
| this.put(Constants.SERVICE_RANKING, 1); |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/*"); |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,"("+HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME+"=default)"); |
| } |
| }); |
| } |
| |
| super.mediator.register( |
| new Hashtable() {{ |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, RestAccessConstants.WS_ROOT); |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,"("+HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME+"=default)");}}, |
| new WebSocketServlet() { |
| private static final long serialVersionUID = 1L; |
| private WebSocketConnectionFactory sessionPool = new WebSocketConnectionFactory(Activator.super.mediator); |
| |
| private final AtomicBoolean firstCall = new AtomicBoolean(true); |
| |
| private final CountDownLatch initBarrier = new CountDownLatch(1); |
| @Override |
| public void init() throws ServletException { |
| mediator.info("The Echo servlet has been initialized, but we delay initialization until the first request so that a Jetty Context is available"); |
| } |
| |
| @Override |
| public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException { |
| if(firstCall.compareAndSet(true, false)) { |
| try { |
| delayedInit(); |
| } finally { |
| initBarrier.countDown(); |
| } |
| } else { |
| try { |
| initBarrier.await(); |
| } catch (InterruptedException e) { |
| throw new ServletException("Timed out waiting for initialisation", e); |
| } |
| } |
| super.service(arg0, arg1); |
| } |
| |
| private void delayedInit() throws ServletException { |
| Thread currentThread = Thread.currentThread(); |
| ClassLoader tccl = currentThread.getContextClassLoader(); |
| currentThread.setContextClassLoader(loader); |
| try { |
| super.init(); |
| } catch(Exception e) { |
| e.printStackTrace(); |
| } finally { |
| currentThread.setContextClassLoader(tccl); |
| } |
| } |
| |
| @Override |
| public void configure(WebSocketServletFactory factory) { |
| factory.getPolicy().setIdleTimeout(1000 * 3600); |
| factory.setCreator(sessionPool); |
| }; |
| }, |
| new Class[]{ Servlet.class, WebSocketServlet.class }); |
| super.mediator.info(String.format("%s servlet registered", RestAccessConstants.WS_ROOT)); |
| |
| super.mediator.register(new HttpLoginEndpoint(mediator), Servlet.class, new Hashtable() {{ |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, RestAccessConstants.LOGIN_ENDPOINT); |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,"("+HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME+"=default)"); |
| } |
| }); |
| super.mediator.info(String.format("%s servlet registered", RestAccessConstants.LOGIN_ENDPOINT)); |
| |
| mediator.register(new HttpRegisteringEndpoint(mediator), Servlet.class, new Hashtable() {{ |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, RestAccessConstants.REGISTERING_ENDPOINT); |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,"("+HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME+"=default)"); |
| } |
| }); |
| super.mediator.info(String.format("%s servlet registered", RestAccessConstants.REGISTERING_ENDPOINT)); |
| |
| mediator.register(new HttpEndpoint(mediator), Servlet.class, new Hashtable() {{ |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, RestAccessConstants.HTTP_ROOT); |
| this.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,"("+HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME+"=default)"); |
| } |
| }); |
| super.mediator.info(String.format("%s servlet registered", RestAccessConstants.HTTP_ROOT)); |
| } |
| |
| /** |
| * @inheritDoc |
| * @see org.eclipse.sensinact.gateway.common.bundle.AbstractActivator# |
| * doStop() |
| */ |
| public void doStop() throws Exception { |
| } |
| |
| /** |
| * @inheritDoc |
| * @see AbstractActivator# |
| * doInstantiate(org.osgi.framework.BundleContext, int, java.io.FileOutputStream) |
| */ |
| @Override |
| public NorthboundMediator doInstantiate(BundleContext context) { |
| NorthboundMediator mediator = new NorthboundMediator(context); |
| return mediator; |
| } |
| } |