I'm studying EJB3.
I have a session bean which provides services to create/update customer accounts.
This session bean offers services on the lines of:
public void addCustomer(Customer c);
public void updateCustomer(Customer c);
Ideally I'd like to have a single servlet: CustomerServlet and it would invoke the session beans that I have listed above.
Problem is that I have two JSPs: UpdateAccount.jsp and CreateAccount.jsp. Both of these JSPs have a form with a method POST and action "CustomerServlet".
How can I distinguish in a customer servlet which operation I should carry out: createAccount or updateAccount?
I guess the alternative is to have a separate servlet for each operation...
Thank you
I'm not really certain about the best practice for this but I have a couple of suggestions that might work:
If your form is being submitted using a submit button, you could distinguish the request on the basis of the value of the <button-name> parameter. So if your buttons had the values Update and Create and were named account-submit, by checking the value you get with request.getParameter('account-submit'), you'd be able to tell which button was clicked to generate this request. If you named them differently, you could also just check which of the two parameters was not null and you'd know which form submit you were handling.
Note that if you have only a single text field in your form and the user hits Enter instead of clicking the button, you'll get a null in your servlet! See my blog post about this behaviour.
Check the Referer header - I wouldn't really recommend this since you wouldn't always know the context of the deployed app, this value may not always be present and it can be easily spoofed.
Add another mapping for your servlet so that it's accessible at both http://myapp.example.com/context/create and http://myapp.example.com/context/update. You can then check the ServletPath (request.getServletPath()) to see what 'servlet' the request came in for. I'd probably go with this one since it seems the most robust to me but you might also want to add the other two checks just to make sure. In your web.xml, you'd want something like
<servlet>
<servlet-name>CreateUpdateServlet</servlet-name>
<servlet-class>my.package.CustomerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CreateUpdateServlet</servlet-name>
<url-pattern>/create</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CreateUpdateServlet</servlet-name>
<url-pattern>/update</url-pattern>
</servlet-mapping>
JSPs are Servlets, just in a different source code form, there is no reason to POST to a different Servlet, you can just POST back to the same JSP.
You don't need the servlet. JSPs (or Facelets) can talk directly to the beans via EL.
Related
The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.
So here is my problem, I am somewhat new to programming, and now I want to make "web.xml" in Java EE project, to rout every url that contains "/profile/" to a profile page, where depending on the id after "/profile/" it'll show current users data. I have the servlet mapped as below...
<servlet-mapping>
<servlet-name>RoutServlet</servlet-name>
<url-pattern>profile/*</url-pattern>
</servlet-mapping>
But what I am supposed to do in the servlet so I can get the id, and show current users data?
Please give me an advice, or nice resource where I can see how it's done.
Thank you in advance ! :)
That looks fine for the web.xml. In short, if you're using a JAX-RS implementation (e.g. Jersey), you should use the #Path annotation with #PathParam for the parameters, so something like:
#Path("/profile")
public class ProfileService {
#GET
#Path("/{id}")
public Profile getProfile(#PathParam("id") String id) {
//...
}
See the Oracle guide on RESTful Web services for some more background.
Firstly I would change tha mapping to the following:
<servlet-mapping>
<servlet-name>RoutServlet</servlet-name>
<url-pattern>/profile</url-pattern>
</servlet-mapping>
Secondly in RoutServlet you should have a the following code to get the userId
int userId = Integer.parseInt(req.getParameter("userId"));
And finally any URL's that sends a request to RoutServlet should look like this
http://someurl.com/profile?userId=//someId
This way you can let RoutServlet retrieve all the information of the User depending on what Id is in the GET request.
I would let RoutServlet put all the data it retrieves in an ArrayList<Object> profileData, redirect to a JSP and retrieve the data there using Expression Language, but I dont know what you use/prefer to display the data.
I'm writing a simple little application, which is a booking system. In the system there are two roles: administrator and user. All requests are working through the only servlet. Index.jsp (loginpage for user and admin) works without its help. So I have the starting url looking like this:
localhost:8080/[AppName]/index.jsp
To ensure security, I wrote a filter that will not allow anonymous users going to any page, except index.jsp; admin go on user page, and user go on admin page. But the problem is that I can't map the filter properly, because all the URLs in my app look like:
localhost:8080/[AppName]/servlet?command=[commandName]
Because of this, such a mapping, like (of course, in the web.xml the filter has already described before this mapping):
<filter-mapping>
<filter-name>Security</filter-name>
<url-pattern>/servlet?command=[commandName]</url-pattern>
</filter-mapping>
does not work, and I don't like it at all, because in this case it is necessary to prescribe all the commands of an application.
In this regard, I have an idea to make the url when smbdy log on like these:
localhost:8080/[AppName]/user - for user
localhost:8080/[AppName]/admin - for admin
In the web-inf folder I have inner folder "pages", in which there are several inner folders: "error", "admin" and "user", which keep jsp pages for these roles and errors.
How to implement the proposed idea? I suspect that this is quite trivial, but I didn't found the answer, because I even have no idea, how to name my issue.
I think i get it now;
-First make initial servlet (index.jsp ain't servlet), once user access your page he is being redirected to it. For example localhost:8080/YouWebAppName/IndexServlet.
IndexServlet check whether session is set, if not it redirects to accessible index.jsp, if the session is set then it checks the role, if the role is set to user it redirects to localhost:8080/YourWebAppName/UserServlet, if the session is set to Admin it redirects to localhost:8080/YourWebAppName/AdminServlet.
In both Admin and User servlet you check in the first place if the session variable equals respectively user/admin.
If there are methods that you want to share in both servlets, make third servlet call it however you want and then UserServlet and AdminServlet should extend that servlet
I've recently had something similiar in my project, try like that:
<servlet-mapping>
<servlet-name>YourServletName</servlet-name>
<url-pattern>/YourServletName/*</url-pattern>
</servlet-mapping>
In the servlet code I've got method:
protected String getActionSegment(HttpServletRequest request) {
String action = request.getRequestURI().
replace(request.getContextPath() +
request.getServletPath(), "").
replace("/", "").
trim();
return action.length() > 0 ? action : "index";
}
That method returns the name of the action, if the Url is localhost:8080/MyWebApp/MyServlet, then by default it redirects to index.jsp (you can change that)
In the servlet execution method you simply define what it should do (and where to redirect) in each case for example:
if(action.equals("admin")) {
//do something
} else if (action.equals("user")) {
//do something else
}
Let me know if that's what you meant.
Btw you can also restrict access to jsp folder and only make it work if servlet redirects to the jsp.
I faced with next task: I have an host, for example host.com and web-application on it.
Application written on Java (JSP/Servlets+Struts2).
I need to implement such HTTP-request to servlet mapping: if user enters address in browser like http://host.com/admin.action, where admin.action - existing action, defined in struts.xml, then render those struts2 action for user. If user enters something like http://host.com/abra-kadabra, (action abra-kadabra notdefined in struts.xml), then pass this request to some servlet or struts action.
Can somebody advice how to do such thing?
Thank you!
You could use Tuckey's very powerful URLRewriteFilter. i.e.
<rule>
<from>^/abra-kadabra$</from>
<to>/admin.action</to>
</rule>
This rule would forward all browser requests on "/abra-kadabra" to "/admin.action" transparent to the user.
Servlet specification doesn't give you many options. You can map your servlet to specific path (/some/specific/path), to all paths under some hierarchy (/dir/*) or to some extension (*.action). Best what you can do is to map your servlet to *.action, and then determine action to be executed based on request.getRequestURI() or request.getServletPath().
I am using Stripes but i'm not sure if this problem is because of that. I have an actionBean with a setter method setSearchView. In this setter, I set a cookie. The problem I'm seeing is that if i call that method from my jsp, the cookie does not get set (i have debugged the code and it does go through the code). If i call the same setSearchView from an action handler, the cookie is set.
Is there something I'm missing? Is this a Stripes thing or a jsp/javabean thing?
I think you are misunderstanding the programming model, I'm guessing you are coming from a CGI/Php background.
Setters/getters on Stripes action beans are used to allow the ActionBean to receive the request parameters (URL parameters in the case of GET requests, form parameters in the case of POST requests) from the browser.
You wouldn't set them manually from JSPs because you wouldn't be putting controller logic in the JSPs but in the ActionBean.
The JSP will only be used to display ('View') any data provided by the controller from the model/view-model and to display input elements to allow the user to provide input. (See MVC on Wikipedia)