I am working on an application that consists of several backend services and a frontend client. The entire application is written in Java, and we use the Apache TomEE webserver to run it.
The backend services expose several APIs, and contain several controllers. Some of these APIs are accessible to the frontend client, and some are for internal communication between backend services.
Logging is very important for this application. There is a requirement that the logging system is always initialized before commencing normal operations (to ensure full traceability). The application uses a secure logging system that requires a key to initialize the logging (the logs are signed using this key to prevent tampering of the logs). There is also a requirement that the logging key should be uploaded to each service. Each backend service has an endpoint for receiving a logging key.
There is a "chicken or egg" type problem. The application needs to be running to receive the key, but also the application should not be fully operational until the key has been received.
To meet the requirements, we are considering the following startup procedure:
- Startup the backend services in a reduced mode of operation, in which the only accessible endpoint in each service is the one for receiving an incoming key.
- Once a key has been received, and the logging system initialized, then active the other endpoints, and commence normal operations.
Is there a standard way of activating endpoints to facilitate this startup process? or anyway of controlling access to endpoints.
Some extra information: the controllers classes within the application do not extend any other class, and are only decorated with the @Path
and @Stateless
annotations.
Update 1
I followed the approach of using a filter (as suggested by Bogdan below). I have created a filter that captures all requests. The application starts up correctly. The init()
method in the filter class is called. But when I access the /installkey
endpoint an error occurs.
What seems to happen is that the doFilter(ServletRequest, ServletResponse, FilterChain)
method is being called, and my code detects that the request is for the /installkey
endpoint. But an error comes from the call: filterChain.doFilter(request, response);
.
I have checked, and I know that the variable filterChain
is not null
, however within the method doFilter(ServletRequest, ServletResponse, FilterChain)
something goes wrong and I cant debug it.
Possibly, I didn't initialized something that needs to be initialized.
I have added the output that I get below.
Now I have the following in my web.xml
:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.company.filter.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
And the following class:
public class LoggingFilter implements Filter {
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
public void doFilter(final ServletRequest request,
final ServletResponse response,
final FilterChain filterChain) throws IOException,
ServletException {
String url = "";
if (request instanceof HttpServletRequest) {
url = ((HttpServletRequest) request).getRequestURL().toString();
}
if (url.endsWith("/installkey/")) {
filterChain.doFilter(request, response);
return;
} else if (loggerConfig.isInitialized()) {
filterChain.doFilter(request, response);
return;
}
}
public void destroy() {
System.out.println("XXXXXXXXXXX Running destroy");
}
}
But I get the following error:
Jan 19, 2016 10:42:25 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [default] in context with path [/vw-ws-rest] threw exception [Error processing webservice request] with root cause
java.lang.NullPointerException
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:240)
at org.apache.openejb.server.cxf.rs.CxfRsHttpListener.doInvoke(CxfRsHttpListener.java:227)
at org.apache.tomee.webservices.CXFJAXRSFilter.doFilter(CXFJAXRSFilter.java:94)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at com.company.filter.LoggingFilter.doFilter(LoggingFilter.java:63)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:957)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Update 2
As an alternative, I tried the approach of using JAX-RS name binding, as suggested by Cássio Mazzochi Molin.
I created the interface:
import javax.ws.rs.NameBinding;
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD })
public @interface TemporarilyDisabled {
}
And I created a filter class as follows:
@Provider
@TemporarilyDisabled
public class LoggingFilter implements ContainerRequestFilter {
@Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
System.out.println("in filter method!");
}
}
And updated my resource controller class as follows:
@Path("installkey")
@Stateless(name = "vw-installKeyResource")
public class VwInstallKeyResource {
@Inject
private Logger LOG;
@EJB
//... some required classes
@POST
@TemporarilyDisabled
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response savePlatformData(final InstallKeyData installKeyData)
throws CryptographicOperationException, DuplicateEntryException {
....
}
}
This application is using Java EE 6, which I cant update. To test this approach, I had to add the following dependency to the application:
<!-- JAX-RS -->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
The code all compiles ok, and the application starts up ok.
But when i access the endpoint (the endpoint that should be caught by the filter), then the filter code is not executed (I never see the print statement in the filter method), and the endpoint is simply executed as normal.
For some reason, the filter is not capturing the request.
I don't know if the problem is related to the fact that the endpoint is a POST. Alternatively, possibly JAX-RS does not find the filter class, it is decorated with @provider, but I dont know if I need to register the filter in any other way.