I have a Tomcat 6 instance running on my local machine.
I have made the following changes in its configuration:
In /conf/context.xml – changed the tag as follows
<Context crossContext="true">
In /conf/server.xml – changed the tag as follows
<Connector port="8080" protocol="HTTP/1.1" emptySessionPath="true"
connectionTimeout="20000"
redirectPort="8443" />
Suppose I have a WAR file named SampleProject.war deployed here which extract to folder SampleProject.
In some servlet in this WAR, say SampleServlet, I write two blocks of code as follows :
ServletContext context1 = session.getServletContext();
and
ServletContext context2 = session.getServletContext().getContext("/SampleProject");
What is the difference between context1 and context2? I thought that both refer to the application context. But if I set some attribute in context1 and access in context2, I don't get the value in context2.
Any help would be appreciated.
I feel your question was slightly misunderstood and that you already had the basic understanding of the API i.e. once a web-app sets its crossContext="true" it could use getContext() to get access to a context that corresponds to some other web-app deployed on the server.
getServletContext().getContext() equals NULL unless <Context crossContext="true">
From what I've understood, your question actually is that in /SameWebApp why
ServletContext context1 = session.getServletContext();
context1.setAttribute("contextAttribute", new Object());
ServletContext context2 = session.getServletContext().getContext("/SameWebApp");
System.out.println(context1.equals(context2)); // prints false, or
System.out.println(context2.getAttribute("contextAttribute")); // prints null (at least they could have been clones)
In just one word, the answer is "Security". Imagine if you couldn't guarantee that an "adminEmail" context attribute has not been tampered with by an evil web-app having its crossContext=true. Your app could potentially help compromise itself as soon as that "Forgot Password" request comes! :)
A Dive into Tomcat internals
Tomcat 7 provides a class ApplicationContext implements ServletContext that returns from getContext("/context-root") as
if (context.getCrossContext()) {
// If crossContext is enabled, can always return the context
return child.getServletContext();
} else if (child == context) {
// Can still return the current context
return context.getServletContext();
} else {
// Nothing to return
return (null);
}
Here context belongs to current web-app and child represents the other web-app. But, hold on, what makes Tomcat call it a child?
These two actually aren't ApplicationContext but instances of StandardContext a class that implements Context but instead of servlet specific things holds Tomcat specific config settings for a web-app like crossContext, hostname, mimeMappings etc. StandardContext.getParent() gives you the Container and hence it has been referred to as a child above.
Anyways, we're interested in the case when child == context is true i.e. getContext() was called on the "/SameWebApp". The call is being delegated to StandardContext.getServletContext() which has been implemented to return a different instance of ApplicationContext.
This is why the attributes you set in context1 are not found in context2.
But wait, there's some more to it. Why does StandardContext.getServletContext() return like
return (context.getFacade());
A Tomcat instance is basically executing two types of Java code:
container provided, and
user deployed
The container code is "Trusted" and may need to run with elevated privileges sometimes. The user code, on the other hand, is not trusted and needs to be restricted from compromising Tomcat internals.
One of the things that Tomcat does to achieve this is always wrap an ApplicationContextFacade around the ApplicationContext (and hence the StandardContext as well). So just to recap, what appears to be a simple ServletContext implementation is actually a StandardContext mapped to an ApplicationContext which is then wrapped within an ApplicationContextFacade.
For further information on how the ApplicationContextFacade works using Reflection in tandem with Globals.IS_SECURITY_ENABLED and SecurityUtil.isPackageProtectionEnabled() settings please take a look at Why do Servlets access Tomcat ApplicationContext through a Facade on SO.
References:
Tomcat 7 Source Code (Download Link)
Absolutely those two context objects are different from another..
Context1 object gives current web application servlet context obj.
( ServletContext context1 = session.getServletContext();)
and
context2 object gives the servletcontext obj of specified web application
(ServletContext context2 = session.getServletContext().getContext("/SampleProject");)
you are setting object in one context and trying to retrieve using another context, so it is not possible to get attribute from another web application context by putting it in current application context. But you can get attribute resides in another web application context by using second method.
Think OO & java EE platform standards + security.
The first call returns the definitive servlet context for the current app, with all operations supported.
The second call returns a copy of the servlet context that could be for any app. As stated (rather vaguely!) in the javadoc, it's purpose is to allow you to obtain a RequestDispatcher, so you can dispatch to pages of other apps. It's other major but implicit requirement is to do this securely and to respect the Java EE specs, which do not allow sharing of session state or servlet context between apps. Imagine the terrible damage "rogue App B" could do to "good App A" if it could just change (or read) Servlet Context data by brute force. That's why it's a copy.
Hence, setting attributes on the copy, does not result in changes to the original. You could argue that the copy should throw some "Operation Not Supported Exception". Alternatively, you could argue that getRequestDispatcher should be refactored either to another class, or to allow an App Context URL to be passed in. ... But, unfortunately, neither of these things are true. B^)
Related
I'm using java7 and seam jboss.
I have set up a conversation-required and no-conversation in my pages.xml to prevent bookmarks from accessing the secured pages. However the issue now is that the Restful services when accessed are always redirecting to the no-conversation-view-id because the when accessing Rest we don't start a conversation.
Is it possible for the Restful component to start a conversation?
I don't want to annotate every Rest method with #Begin.
Thanks,
A context defines a namespace, a set of context variables. These work much the same as session or request attributes in the servlet spec. You may bind any value you like to a context variable, but usually we bind Seam component instances to context variables.
So, within a context, a component instance is identified by the context variable name (this is usually, but not always, the same as the component name). You may programatically access a named component instance in a particular scope via the Contexts class, which provides access to several thread-bound instances of the Context interface:
User user = (User) Contexts.getSessionContext().get("user");
You may also set or change the value associated with a name:
Contexts.getSessionContext().set("user", user);
Usually, however, we obtain components from a context via injection, and put component instances into a context via outjection.
4.1.9. Context search priority
Sometimes, as above, component instances are obtained from a particular known scope. Other times, all stateful scopes are searched, in priority order. The order is as follows:
Event context
Page context
Conversation context
Session context
Business process context
Application context
You can perform a priority search by calling Contexts.lookupInStatefulContexts(). Whenever you access a component by name from a JSF page, a priority search occurs.
Surc:Google
While trying to move an axis2 web-app from glassfish3 to tomcat6, I can't seem to find a way to get a config parameter from a static context.
In glassfish3, a system property was defined in a far away place and read from the application using System.getProperty(String name). Not only does the web agree that this is not the way to go for a web application, this trick is just not feasible for tomcat (tomcat docs).
Reading parameters from the ServletContext is also not feasible as the app uses axis2 and I can't seem to find a way to access any kind of servlet voodoo from the static context that initializes the app's configuration.
services.xml (the file containing the service description for axis2) can contain <parameter> nodes, so that seems a nice place to configure the configuration location, but I can't seem to find a way to read these parameters from the application.
So in short: any ideas on how to get a value configured outside the application's code available from a static context?
(answer listed here as StackOverflow does not allow me to answer my own question...)
After scouring the Internet some more, a solution was found using an implementation of org.apache.axis2.engine.ServiceLifeCycle, which could read a parameter in the startUp-method as such:
Parameter param = service.getParameter("name");
if (param != null) {
saveParamValue(param.getValue().toString());
} else {
// log warning on falling back to System.getProperty()
}
The life cycle class is attached using class="fully.qualified.ClassName" on the <service> node of the services.xml file used by axis2.
This works, now the application just crashes on something else (but that has little to with this issue).
The parameters in services.xml can be accessed by getting the ServiceContext object for the service, then calling ServiceContext.getParameter(). If your service implementation class implements the Lifecycle interface, then Axis2 will call Lifecycle.init() every time it creates a new instance of the service class. The argument to Lifecycle.init() is the service's ServiceContext. Your init() implementation could save the context object or look up the parameters that you're interested in.
Over the last few weeks i have been looking at solutions to share an object between two contexts/war files. There are a number of ways this can be done and one of them is JNDI.
I am not very familiar with JNDI used in Tomcat so would like a few questions clarified:
Basically i have an instance of an object that would provide the following services to more than one context/application
Check that the user is logged on
Check that the user's session is valid
Logon user - Includes logging the logon details
Logout user - Remove session
Each application will call this object to validate the user before it processes any requests. What i dont understand is how the object would work if it is stored on JNDI. I have seen a few examples of how JNDI is used in Tomcat but 99% of the examples show how to configure a JDBC datasource.
How exactly is the object initialised in JNDI. For example, the following configuration from the Tomcat docs shown the configuration for JDBC
<Resource name="jdbc/db1"
auth="Container"
type="oracle.jdbc.pool.OracleDataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
factory="oracle.jdbc.pool.OracleDataSourceFactory"
url="jdbc:oracle:thin:#oracle.microdeveloper.com:1521:db1"
user="scott"
password="tiger"
maxActive="20"
maxIdle="10"
maxWait="-1">
How would i do a similar thing in my case and most importantly, how would i initialise the object before it is put on the JNDI tree. Once it is on the JNDI tree, how is it updated?
I guess what i am looking for is a simple example of the use of JNDI in Tomcat but not for database connections but for service provider type objects.
I have been reading this tutorial http://docs.oracle.com/javase/tutorial/jndi/ops/bind.html but it is focused more on LDAP naming directories which doesnt really help.
Edit
Ok i did find an example in the tutorial i listed above that shows how to "bind" an object the the JNDI tree.
class SerObj {
public static void main(String[] args) {
// Set up environment for creating initial context
Hashtable<String, Object> env = new Hashtable<String, Object>(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
try {
// Create the initial context
Context ctx = new InitialContext(env);
// Create object to be bound
Button b = new Button("Push me");
// Perform bind
ctx.bind("cn=Button", b);
// Check that it is bound
Button b2 = (Button)ctx.lookup("cn=Button");
System.out.println(b2);
// Close the context when we're done
ctx.close();
} catch (NamingException e) {
System.out.println("Operation failed: " + e);
}
}
}
How would i modify it to use the Tomcat environment instead of the LDAP environment as shown above?
Does the bind store a copy of the object or a reference to the object? What i am not sure of is for example, if 3 webapps access the object, will they all be accesssing the same instance of the object or different instances? I would like there to be only one instance of the object i put in the JNDI tree.
Edit
I must be missing something very simple. Here is another example i found which shows how to configure a JNDI object in Tomcat - http://flylib.com/books/en/4.255.1.275/1/
How is the com.jspservletcookbook.StockPriceBean initialised?
If one webapp uses the com.jspservletcookbook.StockPriceBean object and changes its state, is this change reflected across all other webapps that access this object?
And most importantly, how many instances of the com.jspservletcookbook.StockPriceBean will be created - I need it to be the same instance for all webapps that would access the object.
Edit
#Ben Brunk - At the moment the session data is stored in a Map which is managed by one of the applications(context). All i need is for the other applications to call this application to check if the session is valid. My problem is that for these applications to get the services of the application that manages the session data, i need to use JNDI for them to get a reference to the relevant object.
The data will not be stored in JNDI. I just want to use JNDI as a means for the applications to find and use the Session Manager object. The problem though is that everything i read about JNDI leads me to believe that it is used to create new objects every time an object is searched or 'looked up' in the directory.
Here is diagram i think shows exactly what i want to achieve:
Edit
#EJP - Just to clarify, the functions i listed above are just examples of what the shared object might do. These were provided just as an example. All the functions it would do are related to the user and the logged on session. For example things like account information(username, last login etc), password information (expiry date etc), user privileges (i.e. which areas of the application the user is allowed access to).
Given that the question is really related to multi-context communication via JNDI (Or any other means) and not about the requirements of the object it doesn't really make sense to list everything that the object is supposed to do so i just listed examples of what it could do.
Apologies if the way i put the question confused anyone.
If you don't care to use LDAP, just use the database itself by creating tables in there to manage session information. You should be able to find plenty of examples of how to do that because it is a common need in enterprise applications. In fact, I had not seen an example of doing it using the LDAP directory before. It seems like LDAP would be a bad fit for managing sessions across domains because it really isn't designed to be updated a lot.
Just to be clear: JNDI is not a storage mechanism. It is a mechanism for naming resources in an enterprise environment and then having the container manage connections to those resources. You need to store your session data in a database or you could use the filesystem or a NoSQL map type solution.
There are several other ways of accomplishing this.
Use Tomcat Container Managed Authentication in association with JAAS. Then your module becomes a JAAS LoginModule.
Use Tomcat Container Managed Authentication with the Tomcat Single Sign-on module.
I have some configuration stored in the servlet context, sharing among servlets. The current approach is to load the config into a single copy of static hashmap shared by the servlets when the init() method of any servlet is called. But the problem is the servlets just won't die, so the configuration just won't reset even if I changed the configuration in the servlet context. Because the servlet never call init() again, they will not reload the configuration into the hashmap.
I am using websphere 7, is there any place I can configure when does a servlet die, and a new servlet is born?
I think it is too clumsy to reload the configuration from the servlet context when there is a new request.
Edit:
I have a scheduler which will create a new thread to check for a specific data file, if the data file was new, it will create a hashmap(with content of the data file) as an attribute in the servlet context.
Now the servlets will check for the servlet context for the hashmap and copy it to a static hashmap for shared use among servlets when they are just created and call init(). But the problem is that the servlet won't die during the execution, it just make the scheduled action not working.
I am using websphere 7, is there any
place I can configure when does a
servlet die, and a new servlet is
born?
No.
Edit: I have a scheduler which will
create a new thread to check for a
specific data file, if the data file
was new, it will create a hashmap(with
content of the data file) as an
attribute in the servlet context.
Now the servlets will check for the
servlet context for the hashmap and
copy it to a static hashmap for shared
use among servlets when they are just
created and call init(). But the
problem is that the servlet won't die
during the execution, it just make the
scheduled action not working.
Refactor the settings into a separate data object (rather than a HashMap or servlet instance fields) to be shared by servlet instances. Change the scheduler/alarm to update the shared data object directly.
Why not store this data in JNDI, where the servlets can get it whenever they need it? That way, you have complete control of the data; the servlet lifecycle would no longer matter.
I have a servlet which handles http get requests that I'd like to be able to share an object which is also used by a webservice im developing. They are both on the same tomcat server within the same web-app container. Im not sure best how to do it any ideas ?
You can share things across the webapp by storing them as attributes in the ServletContext (using setAttribute / getAttribute). You could create the object in an impelementation of ServletContextListener, store it in the ServletContext, and then retrieve it and use it from your web service and servlet.
I will expand on my comment here.
In the simplest case ServletContext.setAttribute/getAttribute would be fine. But some people rightly raised the questions about thread safety.
For this a better approach would be to store a shared POJO in a ServletContext during webapp initialization and get it wherever you need with ServletContext.getAttribute.
For this you need to implement ServletContextListener interface and declare it in your web.xml file.
<listener>
<listener-class>your.package.ServletContextListenerImpl</listener-class>
</listener>
This listener is called once when your webapp is loaded by the servlet container, and when it is about to be unloaded by the servlet container. In both cases it passes ServletContextEvent that has a handle to ServletContext. It is at that point that you want to set/removeAttribute that points to your shared object.
Because of this you may be certain that ServletContext.getAttribute will return a valid object when called from the Servlet.service or one of the do... methods.
As for attribute name, I would go with your Shared class classname. I would also add a static access method to your shared class to get it from the ServletContext, like this:
public class Shared
{
...
public static Shared fromServletContext ( final ServletContext context )
{
return (Shared) context.getAttribute( Shared.class.getName( ) );
}
}
The simplest option is create a Singleton - a class which allows only one instance in memory. Since you get it by calling a static method on the class itself it should be available to both the servlet and the WS
If my alternative understanding of the question is correct, the data which comes from request should be stored, and then retreived by the web-service. If this is supposed to run in a multi-user environment, you might consider using an in-memory database (HSQLDB) to temporarily store the data. Then you will be able to retrieve it with your web-service, based on some criteria I cannot foretell.
If this is application-specific data (accessible by all users (sessions)), then use ServletContext#set/getAttribute(). If this is user (session)-specific data, then use HttpSession#set/getAttribute().
Let the servlet class set the object in the desired scope (application or session) by a specific attribute key and let the webservice get the object from the desired scope by the same attribute key.
I think you rather need the HttpSession.
The singleton story makes no sense here.