The Jetty 9 application I am developing automatically scans a set of JarFiles for web.xml, then programmatically imports the contained webapps as WebAppContexts. I need to implement single sign-on between the individual webapps, as explained in the following tutorial for Jetty 6: http://docs.codehaus.org/display/JETTY/Single+Sign+On+-+Jetty+HashSSORealm. Unfortunately, HashSSORealm seems to have been removed from Jetty. Are there any viable alternatives for implementing simple SSO?
I did find this post recommending the Fediz jetty plugin, but would prefer to use a native jetty solution if such a thing exists: http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03176.html
Further info:
The central issue seems to be that each WebAppContext must have its own SessionManager, making it impossible for the WebAppContexts to share information with one another even when using the same cookie.
I solved the issue- you simply have to assign the same instance of SessionManager to each WebAappContext's SessionManager. It'll look a little something like this, assuming all WebAppContexts are grouped under the /webapps/ context path:
// To be passed to all scanned webapps. Ensures SSO between contexts
SessionManager sessManager = new HashSessionManager();
SessionCookieConfig config = sessManager.getSessionCookieConfig();
config.setPath("/webapps/"); // Ensures all webapps share the same cookie
// Create the Handler (a.k.a the WebAppContext).
App app = new App(deployer, provider, module.getFile().getAbsolutePath());
WebAppContext handler = (WebAppContext)app.getContextHandler(); // getContextHandler does the extraction
// Consolidating all scanned webapps under a single context path allows SSO
handler.setContextPath("/webapps" + handler.getContextPath());
// Cookies need to be shared between webapps for SSO
SessionHandler sessHandler = handler.getSessionHandler();
sessHandler.setSessionManager(sessManager);
If you share the SessionManager across WebAppContexts, then all of those WebAppContexts share exactly the same session instances. The Servlet Spec says that the WebAppContexts should share session ids, not session contents.
Jan
Related
Basically I created some servlets on startup during the contextInitialized phase when my webapp starts up. But I need to be able to add servlets dynamically while the webapp is still running.
I'm starting to realize this may not be possible, so my next solution was to attempt to redeploy the webapp or restart tomcat. Is it possible to redeploy the app from inside the application? Or even add a servlet after the context has already been initialized?
At this point just being able to redeploy the .war would be enough.
As #JasonArmstrong says, you can trigger a reload using JMX Beans, which are available within the same JVM to any application. You can reload yourself or another application. I'll post code later on to show how to do that.
On the other hand, if all you want to do is deploy an additional servlet, you can do that using standard Java Servlet APIs.
The Java Servlet API ServletContext interface contains a method, addServlet, which allows you to deploy a new servlet. That, in combination with the return value from that method should allow you to deploy a new servlet after the application has been initialized.
For example (in a Servlet)
Servlet servlet = ...; // Make your servlet, here
ServletContext application = getServletContext();
ServletRegistration.Dynamic reg = application.addServlet("MyNewServlet", servlet);
reg.addMapping("/nyNewServlet");
UPDATE 2018-12-07
Apologies for the above text which has now been retracted. Everything about it is true except that you can't deploy new servlets (and, presumably, Filters and other things like that) after the context has been initialized.
== Back to original answer==
Or if you want to reload the context, you can still do it using JMX. The code looks something like this:
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
// Get the JVM's local "server" (there should be only one)
MBeanServer mbs = MBeanServerFactory.findMBeanServer(null).get(0);
// Create an "object name" that points to the application's
// context in Tomcat's MXBean tree. You might want to use
// jvisualvm or a similar MBean explorer to locate your
// application to get the exact right syntax for your context
// in the tree. Tomcat version may affect this value.
ObjectName objectName = new ObjectName("Catalina:j2eeType=WebModule,name=//localhost/contextname,J2EEApplication=none,J2EEServer=none");
// Invoke the "reload" method, which will
mbs.invoke(objectName, "reload", null, null);
The Manager app will allow you to update the application. You may also be able to do something with JMX MBeans.
The Manager app allows you to deploy and redeploy via update=true parameter. You can also start, stop, restart an app from there.
It’s not quite what you were looking for, but it’s pretty easy to set up.
We have a task to certify our application in Tomcat7 which is already certified and running in Websphere.
Our application in websphere has JAAS custom login module and it access HttpServletRequest object via callback handlers as shown below and we set user related information in the session object (which is retrieved from request).
javax.security.auth.callback.Callback callbacks[] = new javax.security.auth.callback.Callback[3];
callbacks[0] = new javax.security.auth.callback.NameCallback(
"Username: ");
callbacks[1] = new javax.security.auth.callback.PasswordCallback(
"Password: ", false);
callbacks[2] = new com.ibm.wsspi.security.auth.callback.WSServletRequestCallback(
"HttpServletRequest: ");
The problem here is that, as you see, the above code is specific to websphere. So, we want to write another custom login module specific to Tomcat. Excuse me, this is designed by someone else.
Now the queustion is how can I access HttpServletRequest/HttpSession object in Jaas login module in Tomcat?
Note: I have tried to use ThreadLocal to pass the user information instead HttpServeletRequest but it appears that it is not advised since threads are reused from Thread Pool in Tomcat and there could be a potential problems (memory leak, user information shared etc) in case if we don't clear the ThreadLocal properly.
In Jboss, it can be get through:
HttpServletRequest request = (HttpServletRequest) PolicyContext
.getContext("javax.servlet.http.HttpServletRequest");
But PolicyContext is in JACC and Tomcat does not support JACC yet. It might be supported in Tomcat 9, but low on Tomcat's priority.
You should not have to obtain the HttpServletRequest in the LoginModule. The LoginModule is the JAAS version of an identity store, which is a database/repository for users, their credentials and optionally groups or roles.
In Tomcat a LoginModule is not the prime identity store type. A Realm is, but Tomcat provides a bridge so you can use a LoginModule anyway. But as you have discovered the usage of JAAS in Java EE is not standard. Servers either don't use JAAS at all, or they use it differently from any other server.
What you're looking for is the authentication mechanism, which is the thing responsible for interacting with the request, response and session. Tomcat has two interfaces for that, one is Tomcat specific, the other is a Java EE standard.
The Authenticator see https://tomcat.apache.org/tomcat-9.0-doc/api/org/apache/catalina/authenticator/package-summary.html
The ServerAuthModule see https://tomcat.apache.org/tomcat-9.0-doc/config/jaspic.html and http://docs.oracle.com/javaee/7/api/javax/security/auth/message/module/ServerAuthModule.html
I am having trouble create a new custom type to the jackrabbit in apache sling using the below code. This worked fine straight on Jackrabbit but not on Apache Sling. Am I doing this correctly for sling? Thanks
The following code gives me a "javax.jcr.InvalidItemStateException: Conflict". I am using a standalone sling and am the only user so there is definitely no conflict.
Repository repository = JcrUtils.getRepository("http://localhost:8080/server");
Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
NamespaceRegistry registry = session.getWorkspace().getNamespaceRegistry();
registry.registerNamespace("my", "http://my.com/v1.0");
CndImporter.registerNodeTypes(new FileReader("C:\\test.cnd"), session);
If you're using Sling, you can avoid all this by putting your CND file in an OSGi bundle (where your java code should reside anyway), with a header that tells Sling where to find it.
Your node types will then be registered automatically when your bundle is activated. For an example of this see the event.cnd file which is declared in a Sling-Nodetypes bundle header that's set in that module's pom.xml (or in any other way if you're not using Maven).
Note also that you shouldn't need JcrUtils.getRepository in Sling anyway, the right way to get a repository is via the SlingRepository OSGi service, which takes care of repository login and configuration in a consistent way for all your Sling components. You can get the repository via a #Reference to a SlingRepository in java code, or get a JCR Session from the Resource that Sling provides to request handlers like servlets and scripts. The Slingbucks sample uses both mechanisms.
The JavaDocs for InvalidItemStateException offer this clue:
Exception thrown by the write methods of Node and Property and by save
and refresh if an attempted change would conflict with a change to the
persistent workspace made through another Session. Also thrown by
methods of Node and Property if that object represents an item that
has been removed from the workspace.
Are you sure that you don't have any old sessions still running, perhaps is a daemon process that hasn't been shut down properly?
Is there anyway of configuring tomcat to point more than one context at a webapp?
I need to point these two urls:
http://server.com/abc
http://server.com/def
to a webapp running under the context abc.
Tomcat does not allow direct configuring of multiple <Context> elements to point to the same path.
So your options are either deploying the same web app twice with different Context (Not great idea)
or create a webapp called def that has one custom servlet filter declared in the web.xml that re-writes all requests to abc.
If your requirement is for a production app, I would recommend having an Apache Web Server before the tomcat so that you can do this and more.
Is it possible to have one appBase served up by multiple context paths in Tomcat?
I have an application base that recently replaced a second application base. My problem is a number of users still access the old context. I would like to serve the, now common, application from a single appBase yet accessed via either context. I took a swing at the low lying fruit and used a symbolic link in the 'webapps' directory... pointing the old context path at the new context path; it works, but feels "cheezy." And I don't like that a database connection pool is created for both contexts ( I would like to minimize the resources for connecting to the database ).
Anyway, if anyone knows of the "proper" way to do this I will greatly appreciate it. I'm using Tomcat 6.0.16 - no apache front end ( I suppose URL rewrite would be nice ).
I'm not sure if the answer above will prevent your webapp from loading twice (as you'd have to deploy it to both new and old context paths), but I could be mistaken. Another option would be to have an extremely simple webapp left in the old context, that does nothing except have one custom servlet filter declared in the web.xml that re-writes all requests to the new path (essentially simulating apache's rewrite rule behaviour). You'd have to write the filter class yourself but it would be quite trivial.
Yes, go into the Tomcat Web Application Manager and scroll down to "Deploy directory or WAR file located on server". For "Context Path (optional):" put in the new context. For "WAR or Directory URL:" put in the same path as your existing app.