/** * 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.hadoop.gateway; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import org.apache.hadoop.gateway.audit.api.Action; import org.apache.hadoop.gateway.audit.api.ActionOutcome; import org.apache.hadoop.gateway.audit.api.AuditService; import org.apache.hadoop.gateway.audit.api.AuditServiceFactory; import org.apache.hadoop.gateway.audit.api.Auditor; import org.apache.hadoop.gateway.audit.api.ResourceType; import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants; import org.apache.hadoop.gateway.config.GatewayConfig; import org.apache.hadoop.gateway.descriptor.GatewayDescriptor; import org.apache.hadoop.gateway.descriptor.GatewayDescriptorFactory; import org.apache.hadoop.gateway.filter.AbstractGatewayFilter; import org.apache.hadoop.gateway.i18n.messages.MessagesFactory; import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory; import org.apache.hadoop.gateway.services.GatewayServices; import org.apache.hadoop.gateway.services.metrics.MetricsService; public class GatewayServlet implements Servlet, Filter { public static final String GATEWAY_DESCRIPTOR_LOCATION_DEFAULT = "gateway.xml"; public static final String GATEWAY_DESCRIPTOR_LOCATION_PARAM = "gatewayDescriptorLocation"; private static final GatewayResources res = ResourcesFactory.get( GatewayResources.class ); private static final GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class ); private static AuditService auditService = AuditServiceFactory.getAuditService(); private static Auditor auditor = AuditServiceFactory.getAuditService() .getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME ); private FilterConfigAdapter filterConfig; private volatile GatewayFilter filter; public GatewayServlet( GatewayFilter filter ) { this.filterConfig = null; this.filter = filter; } public GatewayServlet() { this( null ); } public synchronized GatewayFilter getFilter() { return filter; } public synchronized void setFilter( GatewayFilter filter ) throws ServletException { Filter prev = filter; if( filterConfig != null ) { filter.init( filterConfig ); } this.filter = filter; if( prev != null && filterConfig != null ) { prev.destroy(); } } @Override public synchronized void init( ServletConfig servletConfig ) throws ServletException { try { if( filter == null ) { filter = createFilter( servletConfig ); } filterConfig = new FilterConfigAdapter( servletConfig ); if( filter != null ) { filter.init( filterConfig ); } } catch( ServletException e ) { LOG.failedToInitializeServletInstace( e ); throw e; } catch( RuntimeException e ) { LOG.failedToInitializeServletInstace( e ); throw e; } } @Override public void init( FilterConfig filterConfig ) throws ServletException { try { if( filter == null ) { filter = createFilter( filterConfig ); } if( filter != null ) { filter.init( filterConfig ); } } catch( ServletException e ) { LOG.failedToInitializeServletInstace( e ); throw e; } catch( RuntimeException e ) { LOG.failedToInitializeServletInstace( e ); throw e; } } @Override public ServletConfig getServletConfig() { return filterConfig.getServletConfig(); } @Override public void service( ServletRequest servletRequest, ServletResponse servletResponse ) throws ServletException, IOException { try { auditService.createContext(); GatewayFilter f = filter; if( f != null ) { try { f.doFilter( servletRequest, servletResponse, null ); } catch( IOException e ) { LOG.failedToExecuteFilter( e ); throw e; } catch( ServletException e ) { LOG.failedToExecuteFilter( e ); throw e; } catch( RuntimeException e ) { LOG.failedToExecuteFilter( e ); throw e; } } else { ((HttpServletResponse)servletResponse).setStatus( HttpServletResponse.SC_SERVICE_UNAVAILABLE ); } String requestUri = (String)servletRequest.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME ); int status = ((HttpServletResponse)servletResponse).getStatus(); auditor.audit( Action.ACCESS, requestUri, ResourceType.URI, ActionOutcome.SUCCESS, res.responseStatus( status ) ); } finally { auditService.detachContext(); } } @Override public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain ) throws IOException, ServletException { try { auditService.createContext(); GatewayFilter f = filter; if( f != null ) { try { f.doFilter( servletRequest, servletResponse ); //TODO: This should really happen naturally somehow as part of being a filter. This way will cause problems eventually. chain.doFilter( servletRequest, servletResponse ); } catch( IOException e ) { LOG.failedToExecuteFilter( e ); throw e; } catch( ServletException e ) { LOG.failedToExecuteFilter( e ); throw e; } catch( RuntimeException e ) { LOG.failedToExecuteFilter( e ); throw e; } } else { ((HttpServletResponse)servletResponse).setStatus( HttpServletResponse.SC_SERVICE_UNAVAILABLE ); } String requestUri = (String)servletRequest.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME ); int status = ((HttpServletResponse)servletResponse).getStatus(); auditor.audit( Action.ACCESS, requestUri, ResourceType.URI, ActionOutcome.SUCCESS, res.responseStatus( status ) ); } finally { auditService.detachContext(); } } @Override public String getServletInfo() { return res.gatewayServletInfo(); } @Override public synchronized void destroy() { if( filter != null ) { filter.destroy(); } filter = null; } private static GatewayFilter createFilter( InputStream stream, ServletContext servletContext ) throws ServletException { try { GatewayFilter filter = null; if( stream != null ) { try { GatewayDescriptor descriptor = GatewayDescriptorFactory.load( "xml", new InputStreamReader( stream ) ); filter = GatewayFactory.create( descriptor ); } finally { stream.close(); } } GatewayConfig gatewayConfig = (GatewayConfig) servletContext.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); if (gatewayConfig.isMetricsEnabled()) { GatewayServices gatewayServices = (GatewayServices) servletContext.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); MetricsService metricsService = gatewayServices.getService(GatewayServices.METRICS_SERVICE); if (metricsService != null) { GatewayFilter instrumentedFilter = metricsService.getInstrumented(filter); if (instrumentedFilter != null) { filter = instrumentedFilter; } } } return filter; } catch( IOException e ) { throw new ServletException( e ); } catch( URISyntaxException e ) { throw new ServletException( e ); } } private static GatewayFilter createFilter( FilterConfig filterConfig ) throws ServletException { GatewayFilter filter; InputStream stream; String location = filterConfig.getInitParameter( GATEWAY_DESCRIPTOR_LOCATION_PARAM ); if( location != null ) { stream = filterConfig.getServletContext().getResourceAsStream( location ); if( stream == null ) { stream = filterConfig.getServletContext().getResourceAsStream( "/WEB-INF/" + location ); } } else { stream = filterConfig.getServletContext().getResourceAsStream( GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); } filter = createFilter( stream, filterConfig.getServletContext()); return filter; } private static GatewayFilter createFilter( ServletConfig servletConfig ) throws ServletException { GatewayFilter filter; InputStream stream; String location = servletConfig.getInitParameter( GATEWAY_DESCRIPTOR_LOCATION_PARAM ); if( location != null ) { stream = servletConfig.getServletContext().getResourceAsStream( location ); if( stream == null ) { stream = servletConfig.getServletContext().getResourceAsStream( "/WEB-INF/" + location ); } } else { stream = servletConfig.getServletContext().getResourceAsStream( GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); } filter = createFilter( stream, servletConfig.getServletContext()); return filter; } private static class FilterConfigAdapter implements FilterConfig { private ServletConfig config; private FilterConfigAdapter( ServletConfig config ) { this.config = config; } private ServletConfig getServletConfig() { return config; } @Override public String getFilterName() { return config.getServletName(); } @Override public ServletContext getServletContext() { return config.getServletContext(); } @Override public String getInitParameter( String name ) { return config.getInitParameter( name ); } @Override public Enumeration<String> getInitParameterNames() { return config.getInitParameterNames(); } } }