Whats the difference between ActionContext,ServletContext and ServletActionContext? - java

In Java EE, we often use ActionContext,ServletContext and ServletActionContext, they have similar name, but I don't know the difference between them.
I only know the ServletActionContext inherit the ActionContext.
Someone can explain them?

There have many difference between them.
ServletContext
From the ServletContext's package(javax.servlet.ServletContext) we can know it is standard JavaEE WebApplication class library.
ServletContext provides the standard Servlet run-time environment. Actually is some methods of servlet communicate with web container.
public interface ServletContext {
// Returns the URL prefix for the ServletContext.
public String getServletContextName();
//Returns the context-path for the web-application.
public String getContextPath();
//Returns the ServletContext for the uri.
public ServletContext getContext(String uri);
//Returns the real file path for the given uri.
public String getRealPath(String uri);
public RequestDispatcher getNamedDispatcher(String servletName);
public RequestDispatcher getRequestDispatcher(String uri);
ServletContext is included in the ServletConfig, and ServletConfig often read from servlet or filter's init() method:
servletConfig.getServletContext()
filterConfig.getServletContext()
ActionContext
Comes from Struts, but at first comes from Struts1 and Struts2 they are different.
From Struts1:
A servlet (servlet org.apache.struts.action.ActionServlet) handle all the *.do action.
From Struts2:
The filter(org.apache.struts2.dispatcher.FilterDispatcher) handle all the request.
Because struts1 belongs to servlet scope. struts1 action's essentially is servlet.
struts2 action is normal Java bean, out of the servlet limitations.
The ActionContext makes up the lost WEB environment after the strtus2 action out of the stand servlet framework.
The ActionContext main function:
Provide the WEB context.
Solve the thread security issue.
Solve the incompatibility problem with other Framework(such as: webLogic))
ServletActionContext
As you say, ServletActionContext is ActionContext's subclass.
Its functions is starting at ActionContext, and encapsulate the methods, make it more simple and intuitive.
We can also study its source code:
public class ServletActionContext extends ActionContext implements StrutsStatics {
//HTTP servlet request
public static void setRequest(HttpServletRequest request) {
ActionContext.getContext().put(HTTP_REQUEST, request);
}
public static HttpServletRequest getRequest() {
return (HttpServletRequest) ActionContext.getContext().get(HTTP_REQUEST);
}
//HTTP servlet response
public static void setResponse(HttpServletResponse response) {
ActionContext.getContext().put(HTTP_RESPONSE, response);
}
public static HttpServletResponse getResponse() {
return (HttpServletResponse) ActionContext.getContext().get(HTTP_RESPONSE);
}
//servlet context.
public static ServletContext getServletContext() {
return (ServletContext) ActionContext.getContext().get(SERVLET_CONTEXT);
}
public static void setServletContext(ServletContext servletContext) {
ActionContext.getContext().put(SERVLET_CONTEXT, servletContext);
}
From above we can know the ServletActionContext extends ActionContext.

Related

Spring Boot-Angular - Entering Url in Address Bar results in 404

Need help on the basics -
I have integrated Angular and Spring Boot.
I made production build of the Angular app and copied the 6 files in the Spring boot static resource folder.
By default when I hit localhost:8080 index.html is rendered as Spring boot Automatically registers it as welcome page.
Now when i am inside angular i can navigate to different component via ANGULAR ROUTER and the url is also changing.
But when i copy the same URL for example - localhost:8080/myTask and enter it in url address bar it throws 404 resource not found.
Because it hits the Spring controller first and since there is no mapping for that it fails.
In the class where you have extended WebMvcConfigurerAdapter in Spring Boot, inside addViewControllers method, you should do something like this
#Override
public void addViewControllers(final ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/myTask").setViewName("forward:/");
}
for forwarding, all request, you can do registry.addViewController("/**").setViewName("forward:/");
Update Thanks Jonas for the Suggestion. Since WebMvcConfigurerAdapter is deprecated in Spring 5.0, you can implement the above logic by extending WebMvcConfigurer
// the perfect solution(from jhipster)
#Controller
public class ClientForwardController {
#GetMapping(value = "/**/{path:[^\\.]*}")
public String forward() {
return "forward:/";
}
}
If you don't use Spring MVC (for example, you are using Jersey), you can also solve this by using a javax.servlet.Filter:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class AngularRoutingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
if (shouldDispatch(requestURI)) {
request.getRequestDispatcher("/").forward(request, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
private boolean shouldDispatch(String requestURI) {
/* Exclude/Inlclude URLs here */
return !(requestURI.startsWith("/api") || requestURI.equals("/"));
}
}

How to exclude some url from jersey filter?

I've used jersey to create webservices. I've created request filter using ContainerRequestFilter. I've gone through Jersey Request Filter only on certain URI question but I want to exclude filter for some urls only.
#Provider
public class AuthFilter implements ContainerRequestFilter{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// business logic
}
}
Name binding filters
Instead of excluding URIs from a global filter, you could consider using a name binding filter to select the endpoints your filter will be bound to.
Also check this answer for some examples with name binding filters.
Global filters
If you are still happy with the global filter approach, you could consider using the UriInfo interface to get details about the requested URI. Use one of the following approaches to get an instance of UriInfo:
Using the #Context annotation:
#Provider
public class AuthFilter implements ContainerRequestFilter {
#Context
private UriInfo info;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
...
}
}
Getting it from the ContainerRequestContext:
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
UriInfo info = requestContext.getUriInfo();
...
}
Once you have the UriInfo instance, you'll have access to a bunch of methods that may be useful:
getAbsolutePath(): Get the absolute path of the request.
getBaseUri(): Get the base URI of the application.
getMatchedResources(): Get a read-only list of the currently matched resource class instances.
getMatchedURIs(): Get a read-only list of URIs for matched resources.
getPath(): Get the path of the current request relative to the base URI as a string.
getPathSegments(): Get the path of the current request relative to the base URI as a list of PathSegment.
getRequestUri(): Get the absolute request URI including any query parameters.
relativize(URI): Relativize a URI with respect to the current request URI.
resolve(URI): Resolve a relative URI with respect to the base URI of the application.
For more details, check the UriInfo documentation.
If the requested URI does not match the URIs you want to apply the filter to, simply use a return instruction:
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
UriInfo info = requestContext.getUriInfo();
if (!info.getPath().contains("secured")) {
return;
}
}
Dynamic binding
Another approach is dynamic binding. It allows you to assign filters and interceptors to the resource methods in a dynamic manner. Name binding, mentioned above, uses a static approach and changes to binding require source code change and recompilation. With dynamic binding you can implement code which defines bindings during the application initialization time.
The following example extracted from the Jersey documentation shows how to implement dynamic binding:
#Path("helloworld")
public class HelloWorldResource {
#GET
#Produces("text/plain")
public String getHello() {
return "Hello World!";
}
#GET
#Path("too-much-data")
public String getVeryLongString() {
String str = ... // very long string
return str;
}
}
// This dynamic binding provider registers GZIPWriterInterceptor
// only for HelloWorldResource and methods that contain
// "VeryLongString" in their name. It will be executed during
// application initialization phase.
public class CompressionDynamicBinding implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if (HelloWorldResource.class.equals(resourceInfo.getResourceClass())
&& resourceInfo.getResourceMethod().getName().contains("VeryLongString")) {
context.register(GZIPWriterInterceptor.class);
}
}
}
The binding is done using the provider which implements the DynamicFeature interface. The interface defines one configure method with two arguments, ResourceInfo and FeatureContext.
ResourceInfo contains information about the resource and method to which the binding can be done. The configure method will be executed once for each resource method that is defined in the application. In the example above the provider will be executed twice, once for the getHello() method and once for getVeryLongString() (once the resourceInfo will contain information about getHello() method and once it will point to getVeryLongString()).
If a dynamic binding provider wants to register any provider for the actual resource method it will do that using provided FeatureContext which extends JAX-RS Configurable API. All methods for registration of filter or interceptor classes or instances can be used. Such dynamically registered filters or interceptors will be bound only to the actual resource method. In the example above the GZIPWriterInterceptor will be bound only to the method getVeryLongString() which will cause that data will be compressed only for this method and not for the method getHello().
Note that filters and interceptors registered using dynamic binding are only additional filters run for the resource method. If there are any name bound providers or global providers they will still be executed.
For more details, check the Jersey documentation about filters and interceptors.
Using #NameBinding may be the most elegant approach, but if you just want to exclude a single resource and apply the filter on all others you have to remember putting the binding annotation on all resources. In this case you can use ContainerRequestContext.getUriInfo().getMatchedResources() to check whether the target resource has been matched. This is better than hard-coding a path that might change.
The example below will apply the filter logic on all resources but StatusResource:
public class CorsContainerRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext req) {
if (!matchesStatusResource(req)) {
// filter logic
}
}
private boolean matchesStatusResource(ContainerRequestContext req) {
List<Object> matchedResources = req.getUriInfo().getMatchedResources();
for (Object matchedResource : matchedResources) {
if (matchedResource instanceof StatusResource) {
return true;
}
}
return false;
}
}
As mentioned by others Dynamic bindings can be used instead but it is quite ugly as it is not obvious that the filter wouldn't be applied to all resources.
Probably you can check the URL pattern and abort the request, using getUri
Something like the following
public class AuthFilter implements ContainerRequestFilter{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String path = requestContext.getUriInfo().getPath();
if(path.contains("admin")){
requestContext.abortWith(new Response());
}
}
}
https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/filters-and-interceptors.html#d0e9339

Different ways to get Servlet Context

Could anybody explain me what is the difference between this ways of getting the ServletContext of an HttpServlet?
doGet( HttpServletRequest request, ... ){
getServletConfig( ).getServletContext( );
request.getSession( ).getServletContext( );
getServletContext( );
}
Is there any difference in performance or in the context itself? If so, which is the best way? Are there any other way of retrieving the context?
There's one more.
request.getServletContext();
There's technically no difference in performance, only the request.getSession() will implicitly create the HTTP session object if not created yet. So if this is not done yet, then grabbing the servlet context via the session may take a few nanoseconds longer if the session isn't created yet.
There's also no difference in the returned context. Those methods are all just for convenience and which method to obtain the context depends on the context ;) you're currently sitting in.
If you're sitting in a method invoked by servlet's service() (such as doGet(), doPost(), etc), then just use the inherited getServletContext() method. Other ways only unnecessarily add more characters to the source code.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext context = getServletContext();
// ...
}
If you're sitting in servlet's init(ServletConfig) method, then the inherited getServletContext() isn't available yet as long as you haven't called super.init(config). You'd need to grab it from ServletConfig.
#Override
public void init(ServletConfig config) {
ServletContext context = config.getServletContext();
// ...
}
But much better is to override init() instead. In a decent servlet you usually never need to override init(ServletConfig).
#Override
public void init() {
ServletContext context = getServletContext();
// ...
}
If you're not sitting in a servlet but in e.g. a filter which lacks the inherited getServletContext() method and you only have ServletRequest at hands, then you could grab it from there.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletContext context = request.getServletContext();
// ...
}
Note that this is new since Servlet 3.0. Previously, you'd have to grab it from the session.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletContext context = request.getSession().getServletContext();
// ...
}
This is however not nice if you worry about unnecessary session creation. Hence the introduction of ServletRequest#getServletContext() — although you could also simply extract it from FilterConfig (hey, there's yet another way!).
private FilterConfig config;
#Override
public void init(FilterConfig config) {
this.config = config;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletContext context = config.getServletContext();
// ...
}
And then there are HTTP session listeners where you could listen on a.o. session destroy. There's no other way to obtain the servlet context than via HttpSession#getServletContext().
#Override
public void sessionDestroyed(HttpSessionEvent event) {
ServletContext context = event.getSession().getServletContext();
// ...
}
Here you don't need to worry about unnecessary session creation because it's at that point already created for long beforehand. Do note that there's no ServletRequest anywhere as there's not necessarily means of an active HTTP request during server side session timeout.
As last, there's also ServletContext#getContext() which returns the ServletContext of a different web application deployed to same server (this works only if the server is configured to enable cross context access on the target webapp).
ServletContext otherContext = context.getContext("/otherContextPath");
But this already requires the current ServletContext to start with, for which you should by now already know which way to use to obtain it.

is it possible to call one jax-rs method from another?

suppose i have some jax-rs resource class:
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ResourceA {
#GET
public Something get(#Context UriInfo uriInfo) {
if (...) {
//how to get to ResourceB ?
}
}
}
and i want to conditionally redirect the call to some other jax-rs resource:
public class ResourceB {
#GET
#Path("{identifier}")
public Other get(#PathParam("identifier")String someArg) {
}
}
how do i do this?
note that i dont want this to be visible to the client (so no http redirects) and generally the resource methods i want to redirect to dont share the same signature (they may have path params etc as in the example i gave).
im running jersey 2.6 under apache tomcat (its a spring app, if thats any help)
EDIT - im looking for a jax-rs equivalent of servlet forward. i dont want to do an extra http hop or worry abour instantiating resource classes myself
You can get it using ResourceContext as follows:
#Context
ResourceContext resourceContext;
This will inject the ResourceContext into your Resource. You then get the resource you want using:
ResourceB b = resourceContext.getResource(ResourceB.class);
The Javadoc for ResourceContext is here. You can find a similar question here
I'm not aware of any possibility to do this from a resource method, but if it fits your use case, what you could do is implement your redirect logic in a pre matching request filter, for example like so:
#Provider
#PreMatching
public class RedirectFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
String prefix = "/redirect";
String path = uriInfo.getRequestUri().getPath();
if (path.startsWith(prefix)) {
String newPath = path.substring(prefix.length());
URI newRequestURI = uriInfo.getBaseUriBuilder().path(newPath).build();
requestContext.setRequestUri(newRequestURI);
}
}
}
This will redirect every request to /redirect/some/resource to /some/resource (or whatever you pass to requestContext.setRequestUri()) internally, before the resource method has been matched to the request and is executed and without http redirects or an additional internal http request.

Porting a servlet to a web service - accessing the context?

Consider a simply servlet:
// MyServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
UtilClass.doSomething(getServletContext().getRealPath(SOME_FILE));
}
And the utility class does something with the file:
// UtilClass.java
public String doSomething(String filePath)
{
File f = new File(filePath);
String s = readWhateverFrom(f);
return s;
}
I am now porting the doSomething() function to a web service running under Tomcat and Axis2. How would I port it so that I can still access the context and get access to a file under the servlet?
You should get ahold of your (jax-ws) MessageContext. This would depend on your configuration, but perhaps using
#Resource
private WebServiceContext wsCtx;
and in your method:
MessageContext messageContext = wsCtx.getMessageContext()
ServletContext ctx = (ServletContext)
messageContext.getProperty(MessageContext.SERVLET_CONTEXT);
Edit: Seems like Axis2 (as well as Axis) support the following:
HttpServlet servlet = (HttpServlet)
MessageContext.getCurrentContext().getProperty(HTTPConstants.MC_HTTP_SERVLET);
ServletContext ctx = servlet.getServletContext();
With the following imports:
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
Sounds like a job for a Servlet Filter and a ThreadLocal. Axis is running within a Servlet Context, too. So all you have to do is to implement a custom javax.servlet.Filter, stuffing in the ServletRequest into a ThreadLocal where you can access it from within your utility class. You can get the ServletContext from the FilterConfig.

Categories