How do I get URLs of other servlets from within a servlet - java

I have two servlets (S1 and S2). S1 renders a HTML-Page which acces S2 via an URL (img src="URL"). I know the servlet name of S2, but not the URL. The URL is configured in the web.xml of course, but how can I access that from S1?

Use:
HttpServletResponse.encodeURL(String)
Which in your case should be something like this:
response.encodeURL("/S2");
This method will take care of any URL re-writing that needs to take place to maintain session state and I think will prepend the necessary path info to the URL.
I use JSTL these days so this I'm a little rusty on that last point but if the path info isn't prepended you can get it from the request and add it yourself.

I would guess, that most implementations of the ServletConfig hold that mapping informations (org.apache.catalina.core.StandardWrapper does), but since the ServletConfig-Interface don't provides a getter, you'll have to do some tricks to get it and would bind your application to a specific implementation or application server.
Maybe you just read it from the web.xml. Just select all "servlet-mapping" Elements with the given "servlet-name" and read the "url-pattern". Since this is in the spec, that should work on ever app server out there.
EDIT:
Here is the dirty example. Getting the URL mappings using refelction:
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
try {
Class<?> clazz = config.getClass();
Field configField = clazz.getDeclaredField("config");
configField.setAccessible(true);
StandardWrapper standardWrapper = (StandardWrapper) configField.get(config);
clazz = standardWrapper.getClass();
Field mappingsField = clazz.getDeclaredField("mappings");
mappingsField.setAccessible(true);
List<?> mappings = (List<?>) mappingsField.get(standardWrapper);
System.out.println(mappings);
}catch (Exception e) {
logger.error("", e);
}
}
That works in my JSF, Tomcat environment. The config Object is "org.apache.catalina.core.StandardWrapperFacade" and has a field called "config" which hold a "org.apache.catalina.core.StandardWrapper", which has a field called "mappings".
But as I said, this is a dirty hack!

Perhaps you should supply the URL of the second servlet to the first as a servlet parameter. I realise this means encoding the URL twice in the web.xml (which I really abhor), but to avoid problems you can always build the web.xml as part of your build, and populate from a properties file.
A little bit nasty and fiddly, I appreciate, but in the absence of any cross-container API solution, perhaps it's the most pragmatic solution.

You can do it easily in your doGet() or doPost() Method following is the code to be written in doGet() or doPost() metod of servlet to get the mapping associated with a servlet.
public void doGet(HttpServletRequest request,HttpServletResponse response){
String nameOfServlet = "S2";
ServletContext context = getServletContext();
String[] mappinggs=context.getServletRegistration(nameOfServlet).getMappings().toArray();
//mappings will contain all the mappings associated with servlet "S2"
//print take the first one if there is only one mapping
System.out.println(mappings[0]);
}

Related

How to call a Servlet/Filter before a JSP is executed in CQ5?

In a Spring MVC application we have a Controller that would execute before calling the JSP. The Controller would prefetch some values from the database and set them in the model and forward the control to JSP.
How do I implement this feature in CQ 5? I want the SlingFilter to execute before the JSP is executed. And the JSP is not a page component but a component that appears in the side kick.
Note:
I can do this by writing my own SlingSerlvet that would prefetch my required values and use the RequestDispatcher to forward to the JSP.
But by this method I would have to go through a URL like "/bin/.*". And this is again at a page level I want this kind of functionality at component level.
So to answer your specific question, if you want a filter to be executed before a component is called you would create a filter that is listening to Component level filter scope.
See
http://sling.apache.org/documentation/the-sling-engine/filters.html
You would then have your filter change the incoming request to a SlingServletRequest and determine if the target resource is the one that you are looking for.
However this filter would be executed on every single component that is being included on a page. The reverse process of this that may be useful to you is the ResourceDecorator.
http://sling.apache.org/documentation/the-sling-engine/wrap-or-decorate-resources.html
These are executed when the resource is identified, prior to the servlet and filter calls, which would allow you to verify if a resource is a type that you are interested in, and then allows you to add additional information to the resource object.However this is, once again a service that would be applied to every resource that is identified.
However, if what you are looking for is a filter that is only executed for a specific path, then no. Sling doesn't do that. You mentioned Spring MVC and Spring MVC works on a completely different concept of MVC then what Slings version of MVC does.
EDIT
So in a traditional web app, the servlet would be at a fixed position and all filters are applied prior to the call to that servlet. In Sling you are dynamically wiring servlets together to generate the resulting page. So each time that you are in a servlet and call directly or indirectly the request dispatcher, it's executing the resolution process again and applying a series of filters again before the new servlet is executed.
To prevent a high level filter that needs to applied only to the main request being applied on every single internal dispatch, they came up with the idea of contexts, or chains of filters that are applied at different times and associated with different types of includes.
Here is a basic filter that will log a message when it's called. I did this from memory so you'll need to dink with it.
#SlingFilter(scope = SlingFilterScope.COMPONENT, order = Integer.MIN_VALUE)
public class SampleFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(SampleFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
Resource res = slingRequest.getResource();
if (!(res == null || ResourceUtil.isNonExistingResource(res))) {
LOG.error("this servlet is called before resource {} at path {}", res.getName(),res.getPath());
}
chain.doFilter(request, response);
}
}
The important part of this is scope = SlingFilterScope.COMPONENT take a look at the page I had listed earlier and try out different combinations of slignfilterscope and you'll see how it's being applied at different times. scope = SlingFilterScope.REQUEST would be once at a top level on a per page basis.
JE Bailey's answer is correct as far as Filters are concerned, but I suspect your problem might be solved in a different way that better fits Sling's view of the world.
Sling promotes the use of OSGi services for business logic, and scripts should be a thin layer above that. Moving your logic to OSGi services and calling those from your scripts is the recommended way.
You might also have a look at Sling Models which can include processing steps (with #PostConstruct) before the rendering scripts kick in.
But by this method I would have to go through a URL like "/bin/.*".
You can also register a servlet against a resource type, as well as by path, e.g. (from the Sling documentation):
#SlingServlet(
resourceTypes = "sling/servlet/default",
selectors = "hello",
extensions = "html",
methods = "GET")
public class MyServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
...
}
}
If you remove the "selectors", "extensions" and "methods" parameters on the annotation, this servlet would bind against all calls to sling/servlet/default without requiring binding against a set path.

Servlet Parameters vs Attributes

My question is - if I'm using a gatekeeper servlet to forward pages to other servlets, is it better to let the second servlet refer to the parameters or create attributes for them to refer to?
Say I have a form of:
<form action=www.ermehgerdpuppies.com/Gatekeeper id = puppyForm>
<select name=puppyList>
<option value=cutePuppyServlet_12>CutePuppy
<option value=uglyPuppyServlet_14>UglyPuppy
</select></form>
I submit this form which gets to the Gatekeeper servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (request.getParameterMap().containsKey("puppyList"))
{
String qString = request.getParameter("puppyList");
String[] qsArray = qString.split("_");
request.setAttribute("merPuppy", qsArray[1]);
RequestDispatcher rd = getServletContext().getRequestDispatcher(qsArray[0]);
rd.forward(request, response);
}
}
Which then goes to the cutePuppyServlet (for this example, it goes to cutePuppy)
Now in my cutePuppyServlet.java, I can either refer to the data this way:
request.getParameter("puppyList");
OR
request.getAttribute("merPuppy");
With parameter, I can check if it exists in order to prevent blowing everything up. My question is, which is better for maintainability? Should I just stick with forwarding the parameter or should I create an attribute?
Advantages of using a parameter for the inner servlet:
The nested servlet can stand on its own without the parent servlet, if need be.
Developers are more aware of parameters (I don't know why, but I rarely see request attributes used)
Less code because the container passes them in from client implicitly.
Advantages of using a request attribute:
Includes, forwards, etc. will include them because the request does not change, though its URL may.
This is what attributes are actually meant for, messaging passing between components. Therefore, you are adhering to the servlet design.
At the end of the day, it's not going to matter much. I would pick attributes because I care more about doing things the standard way (even if it is a standard that nobody cares about or follows) than doing it quickly.
If data is already available as a parameter, and is always accessible in your design (that is: your entire request cycle can access the request parameters), and you see no design benefit in setting it as an attribute, then access it as a parameter and forget about setting it as an attribute.
"Less is more", I guess that's the point I'm trying to get across.
I think that the main differentiation is that the "value" part in the pair in attributes can be a Java Object, whereas with parameters it can only be a String.

How to define RequestMapping prioritization

I have a situation where I need the following RequestMapping:
#RequestMapping(value={"/{section}"})
...method implementation here...
#RequestMapping(value={"/support"})
...method implementation here...
There is an obvious conflict. My hope was that Spring would resolve this automatically and map /support to the second method, and everything else to the first, but it instead maps /support to the first method.
How can I tell Spring to allow an explicit RequestMapping to override a RequestMapping with a PathVariable in the same place?
Edit 2: It seems that it would work if the /support mapping came before the /{section} mapping. Unfortunately we have dozens of controllers containing numerous methods with RequestMapping. How can I make sure that the controller with the /{section} mapping is initialized last? Or would a pre-interceptor be the way to go?
Edit 1: This is simplified, I know that having those two RequestMapping alone wouldn't make much sense)
Using Spring you can extend the org.springframework.web.HttpRequestHandler to support your scenario.
Implement the method:
#Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
Use it to analyze the incoming request, determine if the request url is part of your special subset of request url's and forward to the appropriate location.
Ex:
#Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/** You will want to check your array of values and have this data cached **/
if (urlPath.contains("/sectionName")) {
RequestDispatcher requestDispatcher = request.getRequestDispatcher("sections" + "/" + urlPath);
requestDispatcher.forward(request, response);
}
}
And setup your sections such as:
#RequestMapping(value={"/sections/{sectionName}"})
This will not interfere with any of your pre-existing controller mappings.
If 2 these methods are defined in 2 different controllers your problem is that you have 2 controllers mapped to same URL. You do not control the order of controllers initialization right now, so the order is random.
I think you need /support mapping to be initialized before /{section}.
To achieve this try to define that controller "section" depends on controller "support". If this will not help try to put both methods together to one controller and put method mapped to "support" before "section"
I this does not work here is other suggestion. What "section" is? If it can accept limited number of values it should be defined as enum. I believe that in this case everything will work as required if support and section methods are in one controller or in separate controllers.
Good luck.
This not seems to be a problem, this is a valid mapping. If you have a look to http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-uri-templates
In the section 16.3.2 Mapping Requests With #RequestMapping exists two methods doing exactly the same that you are trying.
To be sure that your classes are being compiled try to add a #RequestMapping("/someprefix") at class level to see if the URL is being exposed as you want.
I verify your example locally using the version 3.1.0.RELEASE and no issue were present.
As a workaround (and also to provide a well-understand REST URI add some context to your second mapping:
#RequestMapping(value={"client/support"}) // i.e: if you are working with clients
public ModelAndView getsupport(#PathVariable Long supportId){
// do your code here something here
}
Of course that this is valid if this is the unique controller present in the system, otherwise you must use RequestMapping at class level as I suggested above.
I hope this helps.
I am not seeing this behavior with Spring 3.1.2, it could potentially have been a bug with an older Spring version. Here is a gist which runs through without any issues for me - https://gist.github.com/3802254

Questions about the correct way of using servlets

I want to create a servlet that will allow me to upload image files from the client to the server. I am helping myself with the tutorial i found at apache site:
http://commons.apache.org/fileupload/using.html
On my way i am finding some complications and doubts:
Question 1
I would like my servlet to prepare an object with all the values from the request(included images as byte[]) and pass it to an #EJB that will insert all in the database.
Is that possible? Could you give some pseudo code tips on how to improve my current servlet to do that?
#WebServlet(name="uploadServlet")
public class FileUpload extends HttpServlet {
#EJB
private DBAccessEJB ejb;
private static final long serialVersionUID = -1062753489906645120L;
// Will be triggered when a post method is sent by the user(Example: Form
// submit)
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Check if the request is multipart
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
// Create the object that willBe passed to the EJB
Thing thing = new Thing();
if (isMultipart) {
// If it is multipart, save the items in a FileItemfactory
FileItemFactory factory = new DiskFileItemFactory();
// Create an upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
try {
// Get the files out of the request
List items = upload.parseRequest(req);
Iterator iterator = items.iterator();
while (iterator.hasNext()) {
// Get each of the items inside the items list
FileItem item = (FileItem) iterator.next();
// If the item does not come from a field
if (!item.isFormField()) {
//transform the uploaded images to byte[]
//setTheImageValues of the object
}
else {
//set the text values of the object
}
}
//Pass the prepared object to the EJB to be inserted in DB
ejb.save(thing)
} catch (FileUploadException fue) {
fue.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Question 2
I thought about passing the request to the servlet through the managaged bean, instead from the JSF page, but i don't really know how to do it. Could you give me some tips? I also don't know how to do it in the normal way, from the page,what do you think would be the best way?
This is what i did so far regarding to the managed bean:
public void save() {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletRequest req = (HttpServletRequest)fc.getExternalContext().getRequest();
//What else do i need here to pass the request to the server?
}
This would be at the page inside a multipart form:
<h:commandButton value="Submit" action="myBackingBean.save"/>
Question 3
In my JSF page i have more or less 10 values almost all are Strings. I take them from the JSF and temporary store them in the JSF page. If the servlet could take all the values from the request, there would be no need for this attributes in the backing bean. Do you think is this approach a good thing to do? Will this be process transaction secure, or is there any risk?
Doubt #1 :
It looks like you use the EJB here as a service layer containing a DAO annotated with EJB annotations to make it a session bean. I do not like this approach and you'll run into issues caused by the difference of the EJB world and the HTTP request world.
It is important to note that one of the biggest reasons to use EJB's is to manage transactions and transaction have to remain short, in the order of ms. This is for a number of reasons, like for example locks on the database. However when dealing with http requests with uploads, this is no longer valid.
From another perspective is that a service layer should represent an abstraction of the database model and should show what you can do with the model from a user perspective. The user does not want to save an image to the database, the user wants to add a portrait to his profile.
instead of
ejb.save(thing)
I prefer functions like
profileService.addPortrait(uid, byte[] image);
This explicitely states what it does, and also satisfies the requirement of short transactions. This means the profile entity is available for other requests which may come concurrently (like some status image, or inbox status, ...)
Doubt #2 :
When in Rome, do as the Romans do...
and start by learning some basics of the language. In this case learn JSF from some tutorials.
Doubt #3 :
Intercepting the request parameter in flight between the browser and the JSF component breaks the component architecture and the data hiding principle. It will also bypass any security measures and validation implemented in the server side parts of the JSF components.
If you use the JSF component framework, it makes sense to only ask the values from the components, not from the request itself.
From your 3 doubts I feel you have a bigger doubt : Should I be using JSF?
If it is mandated by your employer : suck it up, and start hitting the books... Learn which problems JSF and EJB's solve and frame your work in terms of those problems.
If you have the freedom to choose : choose a lighter framework, e.g. Spring + Spring MVC. You'll gain experience and encounter those problems at your own pace.
Question 1-
Absolutely you will need Unique Identifiers for your files, but that becomes less complicated if you do things like storing files in folders by date/username, etc...
Here is a basic workflow for your program that you could use, based on what you have shown so far:
Client computer -> FileUploadServlet (utilizing Apache Commons File Upload)
Once inside the FileUploadServlet:
a) Save the information from the request to a Database by way of your EJB including the file name, Mime Type, information, etc...
b) While still inside the servlet, upload the file to your server, or if you need to, use a commercial solution such as Amazon S3 or Google Storage (by way of a Java API such as JetS3t)
Afterwards, return any necessary information to the client.
Question 2 -
What is your reasoning for requesting throught the Bean, why not just make the Servlet the action instead, and collect the information from the request? I would not make the Bean's save method available on the JSF, as that sounds insecure and un-authenticated.
Question 3 -
Same as above, why store information, even if temporarily, when it is available elsewhere?

Is it possible to use a JSP as a template for a servlet?

I've been intermixing JSPs and Servlets in the web app I'm building and I'm starting to find that my more complex JSPs end up containing a lot of code, which flies in the face of all the MVC lessons that have been pounded into me. I know I can do this by just forwarding to the JSP, but this seems like a stupid hack.
What I'd like to do is use a servlet to do processing and then send a set of values to the JSP to render the HTML and return the response. Something along the lines of:
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// ... Do some processing
resp.getWriter.print(renderJSP("mypage.jsp", values));
}
}
I've been poking around Sun's documentation and found this: http://java.sun.com/developer/technicalArticles/javaserverpages/servlets_jsp/
It seems like the JSP Model 2 architecture is exactly what I want to implement, but I cannot find an example of how one can set that up. For technical reasons, I cannot use one of the more advanced template frameworks like Struts.
Is this possible or a lost cause?
Put the object(s) in the request, forward the request to the jsp page and then use the object(s) in the jsp to render the response.
In your servlet,
MyObject obj = ... //build somehow
request.setAttribute("myObject", obj);
RequestDispatcher rd = request.getRequestDispatcher("WEB-INF/jsp/my.jsp");
rd.forward(request, response);
If your result JSP should not be accessed directly from a URL you should hide it inside the WEB-INF directory where it can be accessed only through the forward directive.
Then on your jsp you can have,
<% MyObject obj = (MyObject) request.getAttribute("myObject"); %>
To retrieve the object and used it as needed.
As others suggested, eventually it would be a good idea to learn to use JSTL and maybe an MVC framework like Spring MVC. The tutorial can be found here.
Put Java objects in the Request/Response/Session and use a javax.servlet.RequestDispatcher in your servlet, something like that:
RequestDispatcher dispatcher = request.getRequestDispatcher("/test.jsp");
dispatcher.forward(request,response);
A forward is server-side and the target servlet/JSP receives the same request/response objects as the original servlet/JSP. Therefore, you can pass data between them using request.setAttribute().
The other option is to use response.sendRedirect(String location) which is client-side (this method sends a temporary redirect response to the client) so the location URL receives a new request from the client, and the only way to pass data is through the session or with web parameters (url?name=value).
This is basically what MVC frameworks do (and no, it's not a hack).
You describe forwarding to the JSP as a hack, but really, that's exactly what the MVC frameworks do (Spring MVC and Struts, at least).
The "model" is the request attributes, which the servlet populates; then the JSP just retrieves them to show. You can wrap that in a "ModelAndView" like Spring MVC does, but it's really about it.
You can get more sophisticated on the JSP, of course, parsing request parameters, session attributes or servlet context ("global") attributes. I've found, in general, it's cleaner to let the front controller/servlet marshall all those into request attributes and have the page just pull from them. If you're using JSTL, the difference between request and session can be even blurrier.

Categories

Resources