diff --git a/core/src/main/java/com/flipkart/gjex/core/web/filter/HttpAccessLogFilter.java b/core/src/main/java/com/flipkart/gjex/core/web/filter/HttpAccessLogFilter.java new file mode 100644 index 00000000..22ca58cf --- /dev/null +++ b/core/src/main/java/com/flipkart/gjex/core/web/filter/HttpAccessLogFilter.java @@ -0,0 +1,55 @@ +package com.flipkart.gjex.core.web.filter; + +import com.flipkart.gjex.core.logging.Logging; +import com.flipkart.gjex.core.service.Api; +import com.google.inject.AbstractModule; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Filter for logging http access log requests + * @author ajay.jalgaonkar + * + */ + +@Singleton +@Named("HttpAccessLogFilter") +public class HttpAccessLogFilter implements Filter, Logging { + private long startTime; + @Override + public void init(FilterConfig filterConfig) throws ServletException {} + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + startTime = System.currentTimeMillis(); + chain.doFilter(request, response); + StringBuilder sb = new StringBuilder(); + sb.append(request.getRemoteAddr()).append(" "); + if (request instanceof HttpServletRequest && response instanceof HttpServletResponse){ + HttpServletRequest httpServletRequest= (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + sb.append(httpServletRequest.getRequestURI()).append(" ") + .append(httpServletResponse.getStatus()).append(" ") + .append(httpServletResponse.getHeader("Content-Length")).append(" "); + } else { + sb.append("Did not get HTTP request").append(" "); + } + sb.append(System.currentTimeMillis()-startTime); + error(sb.toString()); + } + + @Override + public void destroy() { + + } +} diff --git a/core/src/main/java/com/flipkart/gjex/core/web/filter/HttpFilterParams.java b/core/src/main/java/com/flipkart/gjex/core/web/filter/HttpFilterParams.java new file mode 100644 index 00000000..9869bc64 --- /dev/null +++ b/core/src/main/java/com/flipkart/gjex/core/web/filter/HttpFilterParams.java @@ -0,0 +1,21 @@ +package com.flipkart.gjex.core.web.filter; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import javax.servlet.Filter; + +/** + * Parameters for creating http filters + * @author ajay.jalgaonkar + * + */ + +@AllArgsConstructor +@Getter +@Builder +public class HttpFilterParams { + private final Filter filter; + private final String pathSpec; +} diff --git a/examples/build.gradle b/examples/build.gradle index 756a8712..390d083a 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -88,7 +88,7 @@ task runHelloWorldServer(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath mainClass = 'com.flipkart.gjex.examples.helloworld.HelloWorldApplication' args "server", "./src/main/resources/hello_world_config.yml" - jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED' +// jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED' } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java b/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java index 61d05163..5ca35123 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java @@ -15,20 +15,24 @@ */ package com.flipkart.gjex.grpc.service; -import java.util.LinkedList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.eclipse.jetty.server.Server; -import org.glassfish.jersey.server.ResourceConfig; - import com.flipkart.gjex.core.logging.Logging; import com.flipkart.gjex.core.service.AbstractService; import com.flipkart.gjex.core.service.Service; +import com.flipkart.gjex.core.web.filter.HttpFilterParams; import com.flipkart.gjex.web.ResourceRegistrar; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.glassfish.jersey.server.ResourceConfig; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; /** * ApiServer is a {@link Service} implementation that manages the GJEX API Jetty Server instance lifecycle @@ -40,19 +44,35 @@ @Named("APIServer") public class ApiServer extends AbstractService implements Logging { - private final Server apiServer; - private final ResourceRegistrar resourceRegistrar; - private List resourceConfigs = new LinkedList(); + private final Server apiServer; + private final ResourceRegistrar resourceRegistrar; + private final ServletContextHandler context; + private final Filter accessLogFilter; + private List resourceConfigs = new LinkedList<>(); @Inject - public ApiServer(@Named("APIJettyServer") Server apiServer, ResourceRegistrar resourceRegistrar) { + public ApiServer(@Named("APIJettyServer") Server apiServer, + @Named("ApiServletContext")ServletContextHandler context, + @Named("HttpAccessLogFilter")Filter accessLogFilter, + ResourceRegistrar resourceRegistrar) { this.apiServer = apiServer; + this.context = context; + this.accessLogFilter = accessLogFilter; this.resourceRegistrar = resourceRegistrar; } public void registerResources(List resourceConfigs) { this.resourceConfigs.addAll(resourceConfigs); } + + public void registerHttpFilters(List httpFilterParamsList){ + context.addFilter(new FilterHolder(accessLogFilter), "/*" , EnumSet.of(DispatcherType.REQUEST)); + for (HttpFilterParams httpFilterParams : httpFilterParamsList){ + context.addFilter(new FilterHolder(httpFilterParams.getFilter()), + httpFilterParams.getPathSpec(), + EnumSet.of(DispatcherType.REQUEST)); + } + } @Override public void doStart() throws Exception { diff --git a/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java b/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java index 6fbb23c3..bd7a5a0c 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java @@ -15,27 +15,19 @@ */ package com.flipkart.gjex.guice; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import com.flipkart.gjex.core.job.ScheduledJob; -import com.flipkart.gjex.grpc.service.ScheduledJobManager; -import io.grpc.health.v1.HealthGrpc; -import org.glassfish.jersey.server.ResourceConfig; - -import io.dropwizard.metrics5.health.HealthCheck; import com.flipkart.gjex.core.Bundle; import com.flipkart.gjex.core.GJEXConfiguration; import com.flipkart.gjex.core.filter.Filter; +import com.flipkart.gjex.core.job.ScheduledJob; import com.flipkart.gjex.core.logging.Logging; import com.flipkart.gjex.core.service.Service; import com.flipkart.gjex.core.setup.Bootstrap; import com.flipkart.gjex.core.setup.Environment; import com.flipkart.gjex.core.tracing.TracingSampler; +import com.flipkart.gjex.core.web.filter.HttpFilterParams; import com.flipkart.gjex.grpc.service.ApiServer; import com.flipkart.gjex.grpc.service.GrpcServer; +import com.flipkart.gjex.grpc.service.ScheduledJobManager; import com.flipkart.gjex.guice.module.ApiModule; import com.flipkart.gjex.guice.module.ConfigModule; import com.flipkart.gjex.guice.module.DashboardModule; @@ -52,10 +44,17 @@ import com.google.inject.Module; import com.google.inject.TypeLiteral; import com.palominolabs.metrics.guice.MetricsInstrumentationModule; - +import io.dropwizard.metrics5.health.HealthCheck; import io.grpc.BindableService; +import io.grpc.health.v1.HealthGrpc; +import org.glassfish.jersey.server.ResourceConfig; import ru.vyarus.guice.validator.ImplicitValidationModule; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + /** * A Guice GJEX Bundle implementation. Multiple Guice Modules may be added to this Bundle. * @@ -73,6 +72,7 @@ public class GuiceBundle implements private List tracingSamplers; private List scheduledJobs; private List resourceConfigs; + private List httpFilterParamsList; private Optional> configurationClass; private GJEXEnvironmentModule gjexEnvironmentModule; @@ -172,7 +172,11 @@ public void run(T configuration, U configMap, Environment environment) { ApiServer apiServer = baseInjector.getInstance(ApiServer.class); // Add all custom web resources resourceConfigs = getInstances(baseInjector, ResourceConfig.class); - apiServer.registerResources(resourceConfigs); + apiServer.registerResources(resourceConfigs); + + // Add all custom http filters + httpFilterParamsList = getInstances(baseInjector, HttpFilterParams.class); + apiServer.registerHttpFilters(httpFilterParamsList); } @SuppressWarnings("unchecked") diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java index aea2a36a..4ff7b63d 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java @@ -19,10 +19,12 @@ import com.flipkart.gjex.core.healthcheck.RotationManagementBasedHealthCheck; import com.flipkart.gjex.core.logging.Logging; import com.flipkart.gjex.core.service.Api; +import com.flipkart.gjex.core.web.filter.HttpAccessLogFilter; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.matcher.AbstractMatcher; import com.google.inject.matcher.Matchers; +import com.google.inject.name.Names; import io.dropwizard.metrics5.health.HealthCheck; import io.grpc.BindableService; import io.grpc.Context; @@ -34,6 +36,7 @@ import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; +import javax.servlet.Filter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.concurrent.Executors; @@ -53,6 +56,7 @@ protected void configure() { requestInjection(methodInterceptor); bindInterceptor(Matchers.any(), new ApiMethodMatcher(), methodInterceptor); bind(HealthCheck.class).to(RotationManagementBasedHealthCheck.class); + bind(Filter.class).annotatedWith(Names.named("HttpAccessLogFilter")).to(HttpAccessLogFilter.class); } @Named("ApiScheduledExecutor")