| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.ambari.server.controller; |
| |
| import org.apache.ambari.server.api.AmbariPersistFilter; |
| import org.apache.ambari.server.orm.entities.ViewEntity; |
| import org.apache.ambari.server.orm.entities.ViewInstanceEntity; |
| import org.apache.ambari.server.security.AmbariViewsSecurityHeaderFilter; |
| import org.apache.ambari.server.view.ViewContextImpl; |
| import org.apache.ambari.server.view.ViewInstanceHandlerList; |
| import org.apache.ambari.server.view.ViewRegistry; |
| import org.apache.ambari.view.SystemException; |
| import org.apache.ambari.view.ViewContext; |
| import org.eclipse.jetty.server.Handler; |
| import org.eclipse.jetty.server.Request; |
| import org.eclipse.jetty.server.SessionManager; |
| import org.eclipse.jetty.server.handler.HandlerCollection; |
| import org.eclipse.jetty.server.session.SessionHandler; |
| import org.eclipse.jetty.servlet.FilterHolder; |
| import org.eclipse.jetty.webapp.WebAppContext; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.web.filter.DelegatingFilterProxy; |
| |
| import javax.inject.Inject; |
| import javax.inject.Provider; |
| import javax.inject.Singleton; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import java.io.IOException; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * An Ambari specific extension of the FailsafeHandlerList that allows for the addition |
| * of view instances as handlers. |
| */ |
| @Singleton |
| public class AmbariHandlerList extends HandlerCollection implements ViewInstanceHandlerList { |
| |
| /** |
| * The target pattern for a view resource request. |
| */ |
| private static final Pattern VIEW_RESOURCE_TARGET_PATTERN = |
| Pattern.compile("/api/(\\S+)/views/(\\S+)/versions/(\\S+)/instances/(\\S+)/resources/(\\S+)"); |
| |
| /** |
| * The view registry. |
| */ |
| @Inject |
| ViewRegistry viewRegistry; |
| |
| /** |
| * Session manager. |
| */ |
| @Inject |
| SessionManager sessionManager; |
| |
| /** |
| * The web app context provider. |
| */ |
| @Inject |
| Provider<WebAppContext> webAppContextProvider; |
| |
| /** |
| * The persistence filter. |
| */ |
| @Inject |
| AmbariPersistFilter persistFilter; |
| |
| /** |
| * The security filter. |
| */ |
| @Inject |
| DelegatingFilterProxy springSecurityFilter; |
| |
| /** |
| * The security header filter - conditionally adds security-related headers to the HTTP response for Ambari Views requests. |
| */ |
| @Inject |
| AmbariViewsSecurityHeaderFilter ambariViewsSecurityHeaderFilter; |
| |
| /** |
| * Mapping of view instance entities to handlers. |
| */ |
| private final Map<ViewInstanceEntity, Handler> viewHandlerMap = new HashMap<ViewInstanceEntity, Handler>(); |
| |
| /** |
| * The non-view handlers. |
| */ |
| private final Collection<Handler> nonViewHandlers = new HashSet<Handler>(); |
| |
| /** |
| * The logger. |
| */ |
| protected final static Logger LOG = LoggerFactory.getLogger(AmbariHandlerList.class); |
| |
| |
| // ----- Constructors ------------------------------------------------------ |
| |
| /** |
| * Construct an AmbariHandlerList. |
| */ |
| public AmbariHandlerList() { |
| super(true); |
| } |
| |
| |
| // ----- HandlerCollection ------------------------------------------------- |
| |
| @Override |
| public void handle(String target, Request baseRequest, |
| HttpServletRequest request, HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| ViewEntity viewEntity = getTargetView(target); |
| |
| if (viewEntity == null) { |
| processHandlers(target, baseRequest, request, response); |
| } else { |
| // if there is a view target (as in a view resource request) then set the view class loader |
| ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); |
| try { |
| ClassLoader viewClassLoader = viewEntity.getClassLoader(); |
| if (viewClassLoader == null) { |
| LOG.debug("No class loader associated with view " + viewEntity.getName() + "."); |
| } else { |
| Thread.currentThread().setContextClassLoader(viewClassLoader); |
| } |
| processHandlers(target, baseRequest, request, response); |
| } finally { |
| Thread.currentThread().setContextClassLoader(contextClassLoader); |
| } |
| } |
| } |
| |
| @Override |
| public void addHandler(Handler handler) { |
| nonViewHandlers.add(handler); |
| super.addHandler(handler); |
| } |
| |
| // ----- ViewInstanceHandler ----------------------------------------------- |
| |
| @Override |
| public void addViewInstance(ViewInstanceEntity viewInstanceDefinition) throws SystemException { |
| Handler handler = getHandler(viewInstanceDefinition); |
| viewHandlerMap.put(viewInstanceDefinition, handler); |
| super.addHandler(handler); |
| // if this is running then start the handler being added... |
| if(!isStopped() && !isStopping()) { |
| try { |
| handler.start(); |
| } catch (Exception e) { |
| throw new SystemException("Caught exception adding a view instance.", e); |
| } |
| } |
| } |
| |
| @Override |
| public void removeViewInstance(ViewInstanceEntity viewInstanceDefinition) { |
| Handler handler = viewHandlerMap.get(viewInstanceDefinition); |
| if (handler != null) { |
| viewHandlerMap.remove(viewInstanceDefinition); |
| removeHandler(handler); |
| } |
| } |
| |
| |
| // ----- helper methods ---------------------------------------------------- |
| |
| // call the handlers until the request is handled |
| private void processHandlers(String target, Request baseRequest, |
| HttpServletRequest request, HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| final Handler[] handlers = getHandlers(); |
| |
| if (handlers != null && isStarted()) { |
| if (!processHandlers(viewHandlerMap.values(), target, baseRequest, request, response)) { |
| processHandlers(nonViewHandlers, target, baseRequest, request, response); |
| } |
| } |
| } |
| |
| // call the given handlers until the request is handled; return true if the request is handled |
| private boolean processHandlers(Collection<Handler> handlers, String target, Request baseRequest, |
| HttpServletRequest request, HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| for (Handler handler : handlers) { |
| handler.handle(target, baseRequest, request, response); |
| if (baseRequest.isHandled()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Get a Handler for the given view instance. |
| * |
| * @param viewInstanceDefinition the view instance definition |
| * |
| * @return a handler |
| * |
| * @throws org.apache.ambari.view.SystemException if an handler can not be obtained for the given view instance |
| */ |
| private Handler getHandler(ViewInstanceEntity viewInstanceDefinition) |
| throws SystemException { |
| |
| ViewEntity viewDefinition = viewInstanceDefinition.getViewEntity(); |
| WebAppContext webAppContext = webAppContextProvider.get(); |
| |
| webAppContext.setWar(viewDefinition.getArchive()); |
| webAppContext.setContextPath(viewInstanceDefinition.getContextPath()); |
| webAppContext.setClassLoader(viewInstanceDefinition.getViewEntity().getClassLoader()); |
| webAppContext.setAttribute(ViewContext.CONTEXT_ATTRIBUTE, new ViewContextImpl(viewInstanceDefinition, viewRegistry)); |
| webAppContext.setSessionHandler(new SharedSessionHandler(sessionManager)); |
| webAppContext.addFilter(new FilterHolder(ambariViewsSecurityHeaderFilter), "/*", AmbariServer.DISPATCHER_TYPES); |
| webAppContext.addFilter(new FilterHolder(persistFilter), "/*", AmbariServer.DISPATCHER_TYPES); |
| webAppContext.addFilter(new FilterHolder(springSecurityFilter), "/*", AmbariServer.DISPATCHER_TYPES); |
| webAppContext.setAllowNullPathInfo(true); |
| |
| return webAppContext; |
| } |
| |
| /** |
| * Get the view that is the target of the request; null if not a view request. |
| * |
| * @param target the target of the request |
| * |
| * @return the view target; null if none |
| */ |
| private ViewEntity getTargetView(String target) { |
| Matcher matcher = VIEW_RESOURCE_TARGET_PATTERN.matcher(target); |
| |
| return matcher.matches() ? viewRegistry.getDefinition(matcher.group(2), matcher.group(3)) : null; |
| } |
| |
| |
| // ----- inner class : SharedSessionHandler -------------------------------- |
| |
| /** |
| * A session handler that shares its session manager with another app. |
| * This handler DOES NOT attempt stop the shared session manager. |
| */ |
| private static class SharedSessionHandler extends SessionHandler { |
| |
| // ----- Constructors ---------------------------------------------------- |
| |
| /** |
| * Construct a SharedSessionHandler. |
| * |
| * @param manager the shared session manager. |
| */ |
| public SharedSessionHandler(SessionManager manager) { |
| super(manager); |
| } |
| |
| |
| // ----- SessionHandler -------------------------------------------------- |
| |
| @Override |
| protected void doStop() throws Exception { |
| // do nothing... |
| } |
| } |
| } |