How to invalidate selected session programmatically? - java

I'm trying to invalidate selected session (with given sessionId) from my web application runing on Jboss 4.2. Everything works perfect from JMX console but
I don't know how to do the same in java code. Here is what i have already created:
MBeanServer server=MBeanServerLocator.locateJBoss();
ObjectName objectName = new ObjectName("jboss.web:host=localhost,path=/,type=Manager");
ManagerBase manager = (ManagerBase)MBeanServerInvocationHandler.newProxyInstance(server, objectName, Manager.class, false);
manager.expireSession("sessionID");
But this code gives this exception:
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy574 cannot be cast to org.apache.catalina.session.ManagerBase
Can You help me?

You have to collect the session in a map check following link :
How can i load Java HttpSession from JSESSIONID?
Find number of active sessions created from a given client IP
How to easily implement "who is online" in Grails or Java Application?

If you are still looking for the answer. That snippet works for me:
MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer();
ObjectName objectName=new ObjectName("jboss.web:type=Manager,path=/test,host=default-host");
// declare signature of the parameter
String[] sig = { "java.lang.String"};
// your session id e.g. A7rOCAlFa+9uCeUfYNjJpd3r.undefined
Object[] opArgs1 = { sessionID };
// call the method
String value = (String) server.invoke(objectName, "expireSession",
opArgs1, sig);
I am working on JBoss-7.1.1.Final. My application is called "test", hence the context root "/test". You should create objectName with name of your application.

Related

Can't get an openNTF Session in java being called from an XPage - Version 10.0.1

I'm trying to get a session in java being called from an XPage. The old school way from version 3.0 use to work great:
(Session) Factory.fromLotus(NotesFactory.createSession(), Session.class, null);
But that's no longer available. I tried the code below that I found but I keep getting the following error:
***NO STACK TRACE*** - A session of type CURRENT was requested but no Factory has been defined for that type in this thread.
***NO STACK TRACE*** - Unable to get the session of type CURRENT from the session factory of type null. This probably means that you are running in an unsupported configuration or you forgot to set up your context at the start
of the operation. If you're running in XPages, check the xsp.properties of your database. If you are running in an Agent, make sure you start with a call to Factory.setSession() and pass in your lotus.domino.Session
java.lang.RuntimeException
at org.openntf.domino.utils.Factory.getSession(Factory.java:1012)
at org.openntf.domino.utils.Factory.getSession(Factory.java:859)
The "make sure you start with a call to Factory.setSession() and pass in your lotus.domino.Session" is misleading as setSession() is also no longer available.
if(!Factory.isStarted()) {
Factory.startup();
}
if (!Factory.isInitialized()) {
System.out.println("Factory.initThread");
Factory.initThread(new Factory.ThreadConfig(new Fixes[0], AutoMime.WRAP_32K, true));
}
Session session = Factory.getSession();
I've installed the OpenNTF API using the Update Site and confirmed they're loaded with the "tell http osgi ss openntf" command. I had an initial issue where it couldn't resolve the Factory, so I copied the four jar files to the ext directory and it solved that issue:
org.openntf.domino.rest_10.0.1.201905061230.jar
org.openntf.domino.xsp_10.0.1.201905061230.jar
org.openntf.domino_10.0.1.201905061230.jar
org.openntf.formula_10.0.1.201905061230.jar
I also tried to hand in a Domino Session in the Factory.startup() call and it still didn't work.
Thanks,
Scott
Domino 10.0.1 FP1
openNTF API: OpenNTF-Domino-API-10.0.1.zip
Code where the issue is. This is being called from an XPage. The call is:
<xp:label value="#{javascript:sessionBean.getCoreUser().getUserId();}"/>
This fires the SessionBean Constructor, which calls the startLogging method. That method checks to see if "DebugMode" is turned on. If yes, all debug statements are written to the OpenLog.
private void startLogging() {
System.out.println("Start OpenLog Logging");
Session session = (Session) ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "openSession");
System.out.println("Session: " + session);
try {
ViewEntry valueEntry = session.getCurrentDatabase().getView("SystemConfig").getFirstEntryByKey("DebugMode");
if (valueEntry != null)
SessionBean.debugMode = ("true".equalsIgnoreCase((String) valueEntry.getColumnValue("SetupValue")) ? Boolean.TRUE : Boolean.FALSE);
System.out.println("Debug Mode set to: " + debugMode);
} catch(Exception e) {
System.out.println("Exception in Start OpenLog Logging");
e.printStackTrace();
}
}
The way I do this nowadays in a servlet plugin is to have this in the service(HttpServletRequest, HttpServletResponse) method:
Factory.setSessionFactory(() -> AppUtil.getSession(), SessionType.CURRENT);
Where AppUtil.getSession() is a method that tries to find a session for the current context, checking an XPages environment if available, then ContextInfo.getUserSession(), and finally calling NotesFactory.createSession() if it can't find anything.
After setting that, Factory.getSession() will use the provided override instead of its normal behavior.
Easiest way is what Jesse has suggested. Use variable resolver. I am getting ODA sessions like this for my application. Here is the updated code to fetch ODA session or domino session as available:
public static Session getSessionAsSigner() {
Session s = (Session) ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "openSessionAsSigner");
if (null == s) {
s = (Session) Factory.getWrapperFactory().fromLotus((lotus.domino.Session)ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "sessionAsSigner"), Session.SCHEMA, null);
}
return s;
}
public static Session getSession() {
Session s = (Session) ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "openSession");
if (null == s) {
s = (Session) Factory.getWrapperFactory().fromLotus((lotus.domino.Session)ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "session"), Session.SCHEMA, null);
}
return s;
}
I have created a Utils class with static functions for this purpose only.

MBean nested object name in JMX

I've noticed that some MBeans have nested keys; how do I make the query to get that key?
The image below shows an example:
Normally, the MBean query is like this: "org.apache.cassandra.metrics:type=CQL,name=RegularStatementsExecuted"
How do I add the additional folder to that query? I've tried the following:
"org.apache.cassandra.metrics:type=Cache,CounterCache,name=Capacity"
"org.apache.cassandra.metrics:type=Cache.CounterCache,name=Capacity"
"org.apache.cassandra.metrics:type=Cache,type=CounterCache,name=Capacity"
Any ideas?
I looked over Java Management Extensions (JMX) Best Practices and it doesn't mention anything about nested keys.
I noticed that I could add scope to the property list when I looked at jconsole:
So, what I used was:
"org.apache.cassandra.metrics:type=Cache,scope=CounterCache,name=HitRate"
It's nice to know that it's not documented anywhere...
To get all session ids of tomcat using JConsole which can be found at :-
Catalina > Manager > localhost > /##07 ( > Operations > listSessionIds )
To get the MBean object name of /##07 just click on it on JConsole and it will show the name.(As shown below)
Java code to fetch all the session Ids:
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
JMXConnector jmxConn = JMXConnectorFactory.connect(url, null);
// Connecting to the MBeanServer
MBeanServerConnection mbsConn = jmxConn.getMBeanServerConnection();
Object sessionIds = mbsConn.invoke(new ObjectName("Catalina:type=Manager,host=localhost,context=/##07"), "listSessionIds", null, null);
System.out.println(sessionIds.toString());
//close jmx connection
jmxConn.close();

MBean "jboss.web:type=Manager,path=/,host=localhost" not found

I'm trying to access the JBoss v4.2 MBean registered as
jboss.web:type=Manager,path=/,host=localhost
using the following code:
ObjectName name = new ObjectName("jboss.web:type=Manager,path=/,host=localhost");
ManagementFactory.getPlatformMBeanServer().getAttribute(name, "activeSessions");
But this code keeps throwing the following exception:
javax.management.InstanceNotFoundException : jboss.web:type=Manager,path=/,host=localhost is not registered.
On the other hand I'm able to see and use this bean using the jmx-console via ...//localhost:8080/jmx-console/ - the MBean is available.
What else is necessary to access the same MBean via code?
Found it...
ObjectName name = new ObjectName("jboss.web:type=Manager,path=/,host=localhost");
this.sessions = new Long((Integer) MBeanServerLocator.locateJBoss().getAttribute(name, "activeSessions"));
I had to find the right MBeanServer... MBeanServerLocator.locateJBoss() solves it.

Tomcat JMX - Connecting to server but cant find the MBean i want

I am trying to write a client utility that to connect to Tomcat via JMX and look at the status of the connection datasource.
I set the following VM arguments in $CATALINA_HOME/bin/setenv.bat and restarted Tomcat
set JAVA_OPTS=-Xms512M -Xmx1024M -XX:MaxPermSize=256M %JAVA_OPTS%
set CATALINA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false %CATALINA_OPTS%
I am not very familiar with JMX so i am just having a play with it to get the feel of it.
The utility i am writing will be running outside of Tomcat. I wrote the following test to try and access datasource Mbean object in Tomcat
but for some reason it is not finding it.
public class GuiMonitor {
public static void main(String[] args){
try{
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
final List<MBeanServer> servers = new LinkedList<MBeanServer>();
servers.add(ManagementFactory.getPlatformMBeanServer());
servers.addAll(MBeanServerFactory.findMBeanServer(null));
System.out.println("MbeanServers " + servers.size());
for(final MBeanServer server : servers){
System.out.println("Server : " + server.getClass().getName());
}
MBeanServer mbsc = ManagementFactory.getPlatformMBeanServer();
System.out.println(mbsc.queryMBeans(null, null));
ObjectName on = new ObjectName("Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"");
System.out.println("ObjectName : " + on.toString());
System.out.println(mbsc.getAttribute(on, "Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\""));
} catch (Exception e) {
e.printStackTrace();
}
}
}
I have a JSP page that i found on the internet which when i upload onto the webapps folder and run it, it displays all of the available
MBeans in Tomcat. The object string/name i used above came from the name that was reported on both the jsp page i used and Jconsole so it does exist.
The output to the above program is shown below
MbeanServers 2
Server : com.sun.jmx.mbeanserver.JmxMBeanServer
Server : com.sun.jmx.mbeanserver.JmxMBeanServer
[com.sun.management.OperatingSystem[java.lang:type=OperatingSystem], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Tenured Gen], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Perm Gen], java.util.logging.Logging[java.util.logging:type=Logging], sun.management.CompilationImpl[java.lang:type=Compilation], javax.management.MBeanServerDelegate[JMImplementation:type=MBeanServerDelegate], sun.management.MemoryImpl[java.lang:type=Memory], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Survivor Space], sun.management.RuntimeImpl[java.lang:type=Runtime], sun.management.GarbageCollectorImpl[java.lang:type=GarbageCollector,name=Copy], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Eden Space], sun.management.GarbageCollectorImpl[java.lang:type=GarbageCollector,name=MarkSweepCompact], sun.management.ThreadImpl[java.lang:type=Threading], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Perm Gen [shared-ro]], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Perm Gen [shared-rw]], sun.management.HotSpotDiagnostic[com.sun.management:type=HotSpotDiagnostic], sun.management.ClassLoadingImpl[java.lang:type=ClassLoading], sun.management.MemoryManagerImpl[java.lang:type=MemoryManager,name=CodeCacheManager], sun.management.MemoryPoolImpl[java.lang:type=MemoryPool,name=Code Cache]]
ObjectName : Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name="jdbc/appdb"
javax.management.InstanceNotFoundException: Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name="jdbc/appdb"
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(DefaultMBeanServerInterceptor.java:1094)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getAttribute(DefaultMBeanServerInterceptor.java:662)
at com.sun.jmx.mbeanserver.JmxMBeanServer.getAttribute(JmxMBeanServer.java:638)
at com.bt.c21.c21mon.C21GuiMonitor.main(C21GuiMonitor.java:39)
A couple of questions
Is the URL correct? I know the port number is correct but i am not sure of the service name. The service name "jmxrmi" i am using on the URL is just one that i saw in one of the examples i have been looking at.
I have a feeling that this is connecting to a different MBeanServer. I suspect this because if you look at the output of mbsc.queryMBeans(null, null), there is nothing tomcat specific. What service name do i use for the Tomcat instance?
If the URL is correct then is the service name always jmxrmi? And why does it not find the "Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"" entry?
I have seen a lot of examples of how to do this and most use a different method to get teh MbeanServer. A couple of examples i have seen are
ManagementFactory.getPlatformMBeanServer()
MBeanServerFactory.findMBeanServer(null)
getMBeanServerConnection()
As mentioned earlier, the utility i am writing is a normal java application that will be running outside of tomcat. Is there any other configuration that i have missed out? I have been looking at several examples and the majority talk about creating MBeans and there is usually references to Listeners. As i am not creating any new Mbeans but only reading the values of existing ones, do i need to configure a listener?
Edit
It seems that getPlatformMbeanServer() is not returning the correct JVM Instance. I tried the following
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
System.out.println("Query2 : " + conn.queryMBeans(null, null));
And this does return some Tomcat specific values. But i am still unable to get the jdbc/appdb datasource.
krtek - I wont be able to use JMX Console as i plan to do it all manually with the intention of automating it.
Edit 2
Ok, i figured out what i was doing wrong. Initially i was trying to retrieve the values as
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName on = new ObjectName("Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"");
mbsc.getAttribute(on, "Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\""));
The above is wrong because the second parameter for mbsc.getAttribute is supposed to be the attribute in the Mbean not the String name.
This gave me the correct attribute values
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName on = new ObjectName("Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource,name=\"jdbc/appdb\"");
mbsc.getAttribute(on, "numIdle")
And i also changed the MBeanServer i was using from getPlatformMbeanServer() to getMBeanserverConnection(). I must admit i dont quite understand the difference because since Tomcat is running on the same JVM as the one returned by getPlatformMbeanServer(). Does it mean that getPlatformMbeanServer() will only return sun specific Mbeans? and getMBeanserverConnection() will include both?
Thanks
That's because you are getting instance of JMX server for your client JVM, not the Tomcat one.
This is right:
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
But you should continue with something like:
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
Set result = conn.queryMBeans(null,
"Catalina:type=DataSource,path=/appdb,host=localhost,class=javax.sql.DataSource");
To test your query string use some tool like JMX console.

Tomcat: Change the Virtual hosts programmatically?

Tomcat offers a build in "Virtual Hosting" Support: An Engine/Web-Application can be configured to be responsible for a list of Domains. These Domains have to be put into the server.xml/context.xml files with a special xml directive.
=> Is there any possibility to change the Tomcat Configuration (in general) and especially the "Virtual Hosts" of a Web-Application/Engine programmatically?
For example if a new user signs up, I have to add his domain to the list of "accepted virtual hosts/domains". The only way I currently think of is changing the xml files via a script and then restart Tomcat.
Is there any way to add them add runtime via some Java-Methods programmatically?
Thank you very much!
Jan
Tomcat provides APIs to create new virtual host. To get access to the wrapper object needed for this, you need to implement a ContainerServlet. You can create virtual host like this,
Context context = (Context) wrapper.getParent();
Host currentHost = (Host) context.getParent();
Engine engine = (Engine) currentHost.getParent();
StandardHost host = new StandardHost();
host.setAppBase(appBase);
host.setName(domainName);
engine.addChild(host);
You need to make sure appBase directory exist and you have to find ways to persist the new host to the server.xml or you lose the host on restart.
However, this approach rarely works. If your users run their own apps, you really want run separate instances of Tomcat so you can sandbox the apps better. e.g. One app running out of memory doesn't kill all other apps.
If you provide the app, you can just use one host (defaultHost). You can get the domain name from Host header and do whatever domain-specific stuff in your code.
You shouldn't change the server environment programmatically and there are no reliable and standard ways to do this. Best is to do and keep it all on the webapp side. To start, a Filter is perfectly suitable for this. Store the names somewhere in a database table or a properties file which you cache in the application scope. Check the HttpServletRequest#getRequestURI() (or the getServerName() if it is a subdomain instead of pathinfo) and do the forwarding task accordingly.
Hope this helps.
Use JMX
ArrayList serverList = MBeanServerFactory.findMBeanServer(null);
MBeanServer server = (MBeanServer) serverList.get(0);
Object[] params = { "org.apache.catalina.core.StandardHost", hostName };
String[] signature = { "java.lang.String", "java.lang.String" };
server.invoke(new ObjectName("Catalina:type=Engine"), "addChild", params, signature);
If needed, retrieve the host object and work with it:
ObjectName host = new ObjectName("Catalina:type=Host,host=" + hostName);
server.setAttribute(host, new Attribute("autoDeploy", false));
server.invoke(host, "start", null, null);
I would suggest you set your application to be the default virtual host in server.xml so your single virtual host can respond to requests addressed to any host name. Tomcat ships with the localhost application set as the default virtual host. So you can see how to do this by simply inspecting the server.xml file of a vanilla tomcat installation. You can programatically determine the host name the user sent the request to using the ServletRequest.getServerName() method.
Tomcat used to ship with a web application called "host-manager". Note: this is different than the "manager" web application that still comes with Tomcat. Host manager allowed for changing configuration or adding new virtual hosts on the fly without restarting the server. You could interact with the host-manager over HTTP (programmatically if desired). However, it had the unfortunate flaw of not committing its changes to server.xml so they were all lost on a web server restart. For whatever reason, starting with version 6, Tomcat no longer ships with the host-manager application. So it doesn't appear to be supported anymore.
To sum up ZZ Coder answer which guided me a lot:
You have to create a servlet that implements ContainerServlet and override setWrapper method to get the org.apache.catalina.Wrapper object.
For doing that you have to have privileged="true" in your context.xml Context tag or it will throw an exception. Then you can use the Wrapper object and:
StandardContext context = (StandardContext) wrapper.getParent();
StandardHost currentHost = (StandardHost) context.getParent();
StandardEngine engine = (StandardEngine) currentHost.getParent();
StandardHost host = new StandardHost();
host.setAppBase(currentHost.getAppBase()); //in my case I created another instance of the same application
host.setDomain(currentHost.getDomain());
host.setAutoDeploy(false); // not restarting app whenever changes happen
host.setName("domain.com");
host.setThrowOnFailure(true);// tell it to throw an exception here if it fails to create the host
host.setDeployOnStartup(true);
host.setStartChildren(true);
host.setParent(engine);
// you can add multiple aliases
host.addAlias(alias);
StandardContext ctx = new StandardContext();
ctx.setDocBase(context.getDocBase()); //again I reused my same application setting
ctx.setPath("");
if(currentHost.getWorkDir() != null)
{//create a working directory based on your new host's name
ctx.setWorkDir(currentHost.getWorkDir().replace(currentHost.getName(), host.getName()));
}
ctx.setName(host.getDomain());
//some extra config that you can use
ctx.setUseHttpOnly(false);
ctx.setReloadable(false);
ctx.setXmlValidation(false);
ctx.setXmlNamespaceAware(false);
ctx.setCrossContext(false);
ctx.setParent(host);
// you have to have this or it will not work!!
ctx.addLifecycleListener(new ContextConfig());
//you can also create resources and add it to the context like so:
final ContextResource res = new ContextResource();
res.setName("name");
res.setAuth("Container");
res.setType("javax.sql.DataSource");
ctx.getNamingResources().addResource(res);
host.addChild(ctx);
engine.addChild(host);
You can add properties to your resource by calling res.setProperty("name", "value")
Some properties that you can use are:
initialSize,maxTotal,maxIdle,maxWaitMillis,removeAbandonedOnBorrow,removeAbandonedTimeout,validationQuery,timeBetweenEvictionRunsMillis,driverClassName,url,username,password.
Another exciting thing to is to get the host from the tomcat engine by calling engine.findChild(domain) and use stop(), start(), getStateName() and have your own Tomcat Admin panel!

Categories

Resources