I need to create add servlets at runtime. When I run the following code.
protected void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println("<html>");
out.println("<head>");
out.println("<title> URI out</title>");
out.println("</head>");
out.println("<body>");
Integer generatedKey = Math.abs(randomiser.nextInt());
out.print(generatedKey);
createServlet(Integer.toString(generatedKey),request.getServletContext());
} finally {
out.println("</body>");
out.println("</html>");
out.close();
}
}
private void createServlet(String generatedKey, ServletContext servletContext) {
String servletMapping = "/"+generatedKey;
ServletRegistration sr = servletContext.addServlet(generatedKey, "com.path.lbs.servlets.testDynamic");
sr.setInitParameter("keyname", generatedKey);
sr.addMapping(servletMapping);
}
I get the following error.
java.lang.IllegalStateException:
PWC1422: Unable to configure mapping
for servlet 1114600676 of servlet
context /123-LBS, because this servlet
context has already been initialized
Is it impossible to add new servlets at runtime i.e. after the Servlet Context is initialised or am I doing something wrong?
Is it impossible to add new servlets at runtime i.e. after the Servlet Context is initialised?
That's correct. You need to do it in ServletContextListener#contextInitialized().
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
// Do it here.
}
#Override
public void contextDestroyed(ServletContextEvent event) {
// ...
}
}
However, for your particular functional requirement, a single controller servlet in combination with command pattern is much better suited. You could then add commands (actions) during runtime and intercept on it based on the request URI. See also my answer on Design Patterns web based applications for a kickoff.
Related
How to make jersey and #webservlet working together ?
jersey ResourceConfig:
#ApplicationPath("/*")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
register(Greetings.class);
}
}
jersey Resource registered in resourceConfig:
#Path("/login")
public class Greetings {
#GET
public Response getHelloGreeting(#Context HttpServletRequest httpRequest) {
System.out.println("In the Greetings resource");
String url= "http://"+httpRequest.getServerName()+":"+httpRequest.getServerPort()+httpRequest.getContextPath();
String newURL = url+"/login.jsp";
System.out.println(newURL);
return Response.seeOther(URI.create(newURL)).build();
}
}
web servlet
#WebServlet(name = "LoginServlet", urlPatterns = { "/hello" })
public class LoginServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext servletContext = getServletContext();
System.out.println("inside login servlet");
request.getRequestDispatcher("/login.jsp").forward(request, response);
System.out.println("request forwarded");
}
//other functions not important so deleted
}
Case 1: on accessing this
http://localhost:8088/ResponseFilterweb/login
console logs:
In the Greetings resource
http://localhost:8088/ResponseFilterweb/login.jsp (no ui comes)
on accessing this
http://localhost:8088/ResponseFilterweb/hello
(nothing happens 404 error)
Case 2: Changing application config resource path:
#ApplicationPath("/auth")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
register(Greetings.class);
}
}
on accessing this
http://localhost:8088/ResponseFilterweb/auth/login
In the Greetings resource
http://localhost:8088/ResponseFilterweb/login.jsp (Ui comes)
on accessing this
http://localhost:8088/ResponseFilterweb/hello
inside login servlet (Ui comes)
userid is
Encoded string
request forwarded
doubts:
don't know why login.jsp is blocked in the first case:
why http://localhost:8088/ResponseFilterweb/login not showing any ui .. i think it should come ?
why http://localhost:8088/ResponseFilterweb/hello not showing any ui ?
If you were using web.xml, this or this would be your solution, but since you're not, this might be your only option. The problem is that when you use /* as the servlet mapping for Jersey, it hogs up all the requests. So the request to /hello would go to Jersey and not the LoginServlet. What those solutions I linked to do is cause Jersey to forward the request if it can't find it within the Jersey application. The other solution is to just change the servlet mapping to something like /api/* (which is pretty common), then you would just prefix your API requests with /api.
Using asterisk (*) won't work using #ApplicationPath
If you use /*, then you're making it too greedy and saying it should match everything all the time, and the default servlet will never be invoked
Use #ApplicationPath("/") instead
If you use /, then you're replacing the container's default servlet
Following BalusC's instructions on that answer:
How to stream audio/video files such as MP3, MP4, AVI, etc using a Servlet
I added the following Context element to my Tomcat server.xml to make my media files available to Tomcat's own DefaultServlet.
<Context docBase="/home/jwi/media" path="/service/media" />
This works like charm and the media is available at:
http://localhost:8080/service/media/example.mp4
The ApplicationPath from my application (build on Jersey 2.x) is set to: #ApplicationPath("service").
Within that application I have a request filter that checks every incoming request for a valid user session.
#Provider
#PreMatching
#Priority(1)
public class SessionFilter implements ContainerRequestFilter {
#Context
private ServletContext _context;
#Context
private HttpServletRequest _request;
#Context
private HttpServletResponse _response;
public void filter(ContainerRequestContext requestContext) throws IOException {
HttpSession session = _request.getSession(false);
boolean isLoggedIn = session != null && session.getAttribute("username") != null;
boolean isLoginRequest = _request.getRequestURI().contains("login");
if (isLoggedIn || isLoginRequest) {
// Since filter chain is invoked by #Priority annotation here's nothing to do.
} else {
URI indexPage = UriBuilder.fromUri("/index.html").build();
requestContext.abortWith(Response.temporaryRedirect(indexPage).build());
}
}
}
My problem is, that filter is never called on the media elements. So when I open http://localhost:8080/service/media/example.mp4 the filter isn't called at all.
How do I add Tomcat's DefaultServlet to my request filter?
Have you considered a Servlet Filter instead?
#WebFilter("/service/media/*")
public class SessionFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
For a WebApp, I need to serve all .js, .css and all images from corresponding folders in my web app development tree ./js, ./css, ... through a default handling.
So any URL like
http://www.example.com/js/x.js
should be served straight from the static files in the war.
The main servlet should deal with all requests that are not for the above.
I need to be able to process requests like
http://www.example.com/PROJ/ELEM/WHATEVER
with the same unique main servlet.
So I thought I'd do this in the web.xml:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/css/*,/js/*,/WEB-INF/*</url-pattern>
</servlet-mapping>
and map the main servlet like this to make some JSTL mods in a JSP file:
#WebServlet(urlPatterns="/*")
public class Main extends HttpServlet {
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("test", "ok");
request.getRequestDispatcher("/WEB-INF/index.jsp")
.forward(request, response);
}
}
When I do this I end up in a recursive loop.
Is there a way to achieve this?
Here is the explanation with same problem.
http://www.kuligowski.pl/java/rest-style-urls-and-url-mapping-for-static-content-apache-tomcat,5
This is what already happens. There is a 'default servlet' that handles any request that isn't specifically mapped to an installed servlet.
A simple variation on Rahul Jain's answer. You could do what spring MVC does for static resources : DispatcherServlet is a catch all, and it is configured to delegate to default servlet for a number or url. This may be interesting for a catch all servlet because it is often designed as a front controller that delegates actual serving to other controllers (be them servlets or not).
You can simply give the prefixes of urls that should serve static resources in a comma separated string in a context param of name static_path_prefixes and put that in your servlet :
String[] staticPathPrefixes;
RequestDispatcher defaultDispatcher;
#Override
protected void service(HttpServletRequest hsr, HttpServletResponse hsr1) throws ServletException, IOException {
String path = hsr.getServletPath();
for (String url: staticPathPrefixes) {
if (path.startsWith(url)) {
defaultDispatcher.forward(hsr, hsr1);
return;
}
}
super.service(hsr, hsr1);
}
#Override
public void init() throws ServletException {
String urls = getServletConfig().getInitParameter("static_path_prefixes");
staticPathPrefixes = urls.split(" *, *");
defaultDispatcher = getServletConfig().getServletContext().
getNamedDispatcher("default");
}
In my servlet:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
new Action();
}
In my Action class:
Action(){
System.out.println("--> " + this.getClass().getResource("/").toString());
}
I have on result, this location:
--> file:/C:/Users/saad/Desktop/apache-tomcat-7.0.37/wtpwebapps/Serveur/WEB-INF/classes/
But i would like to get access to the root of my webApp like this:
file:/C:/Users/saad/Desktop/apache-tomcat-7.0.37/wtpwebapps/Serveur/
I insist that the Action class does not inherit from the HttpServlet, so i can't use ServletContext.getResource("") .
How can i do this ?
You can get the actual file system path of a web resource by using ServletContext.getRealPath() method.
ServletContext ctx = request.getServletContext();
String path = ctx.getRealPath("/");
And modify your Action to take either the request or the servlet context as a parameter:
public Action(ServletContext ctx) {
System.out.println("--> " + ctx.getRealPath("/"));
}
I have two systems I'm trying to integrate. One is built on raw servlets, the new one is build on JSF with IceFaces. I'm trying to facilitate cross-system sign on. The idea is that I have a button in the old system that POSTs the appropriate information to the new site and logs them on.
Well, ideally, I'd like to use just a regular old servlet to facilitate that on the new site. Go to the new site's Servlet, do what it needs to do and the forward onto the dashboard.
Our security is handled via a managed bean. However, by the time you get to the Servlet, there is no faces context. So, how would I create a new faces context?
I have a back up plan in that I can always link to a dummy .iface page which will create the FacesContext for me and then create a backing bean that will do stuff when it gets instanciated and then forward onto the main page. But this feels very much like a hack.
Any help would be appreciated!
EDIT: I went with the back up way. Basically, I POST to a page like so:
<f:view>
<ice:outputText value="#{EntryPoint}"/>
</f:view
The backing bean looking like so...
public EntryPoint() {
try {
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
String loginID = request.getParameter("loginID");
//Do some code to load the user/permissions
response.sendRedirect(
//The appropriate page
);
} catch (IOException ex) {
logger.error(null, ex);
} catch (SQLException ex) {
logger.error(null, ex);
}
}
This still feels like a hack, but I'm not sure how to get around this. Ideally, I'd POST to a servlet, get the loginID, build the user and put it directly into the managed bean. But, the FacesContext does not exist at that point.
Any other ideas?
I'm not sure what you mean by "site" in this context.
A couple of notes:
Managed beans will be never be available outside the web app (WAR) in which they are defined.
FacesContext object instances are ultimately created and disposed by FacesServlet.service, so requests should pass through this servlet. Attempting to create a context under other circumstances might lead to undefined behaviour.
Bearing that in mind, you could create a request sequence like this:
FacesServlet (mapping: /faces/*)
-> /faces/jsfPage.jsp (a JSP with JSF controls)
-> DispatchBean (calls ExternalContext.dispatch("/AnotherServlet")
-> AnotherServlet
jsfPage.jsp contains:
<f:view>
<h:outputText value="#{dispatchBean.dispatch}" />
</f:view>
The "dispatch" property resolves to a bean method "getDispatch":
public String getDispatch() {
FacesContext context = FacesContext.getCurrentInstance();
try {
context.getExternalContext().dispatch("/FacesClientServlet");
} catch (IOException e) {
throw new FacesException(e);
}
return null;
}
Which dispatches to this servlet:
public class FacesClientServlet extends javax.servlet.http.HttpServlet
implements javax.servlet.Servlet {
static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
ExpressionFactory expressionFactory = context.getApplication()
.getExpressionFactory();
ValueExpression expression = expressionFactory.createValueExpression(
elContext, "#{myBean.text}", Object.class);
Object value = expression.getValue(elContext);
ResponseWriter writer = context.getResponseWriter();
writer.write("" + value);
}
}
Which emits the value from a managed bean "myBean":
public class MyBean {
private final String text = "Hello, World!";
public String getText() {
return text;
}
}
This is all very convoluted and I wouldn't willingly do any of it.
An alternative, which may come with its own consequences, is to create your own context like this:
public class ContextServlet extends javax.servlet.http.HttpServlet implements
javax.servlet.Servlet {
static final long serialVersionUID = 1L;
private FacesContextFactory facesContextFactory;
private Lifecycle lifecycle;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
facesContextFactory = (FacesContextFactory) FactoryFinder
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
lifecycle = lifecycleFactory
.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
FacesContext context = facesContextFactory.getFacesContext(
getServletContext(), request, response, lifecycle);
try {
ELContext elContext = context.getELContext();
ExpressionFactory expressionFactory = context.getApplication()
.getExpressionFactory();
ValueExpression expression = expressionFactory
.createValueExpression(elContext, "#{myBean.text}",
Object.class);
Object value = expression.getValue(elContext);
PrintWriter pw = response.getWriter();
try {
pw.write("" + value);
} finally {
pw.close();
}
} finally {
context.release();
}
}
}
Again, I would avoid this approach if possible.