I'm implementing a servlet as a JMX manager that runs in the same instance of Tomcat that all of the monitored servlets are running in. I can see the data of the monitored servlets when I open JConsole. From within my manager servlet I can enumerate all of the available standard MBeans, including the ones I've created in the monitored servlets, using this code like this:
JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi://localhost:1099/jndi/rmi://localhost:1099/jmxrmi" );
mConnector = JMXConnectorFactory.connect( url );
mMBSC = mConnector.getMBeanServerConnection();
mObjectName = new ObjectName( "com.blahCompany.blah.blah:type=BlahBlah" );
// just looking for one specific bean
Set<ObjectName> myMbean = mMBSC.queryNames( mObjectName, null );
if( myMBean.size() == 1 ) // I know it exists
{
MBeanInfo mbeanInfo = mMBSC.getMBeanInfo( <ObjectName extracted from Set> );
MBeanAttributeInfo[] mbeanAttributeInfos = mbeanInfo.getAttributes();
for( MBeanAttributeInfo attribInfo : mbeanAttributeInfos )
{
if( attribInfo.isReadable() )
{
String attribName = attribInfo.getName();
String attribReturnType = attribInfo.getType();
// The data's somewhere ... where????
// In the MBeanInfo?
// In the MBeanAttributeInfo??
}
}
}
The problem is I don't know how to actually extract the data from these MBeans. The answer must be godawful obvious because no one else seems to have asked, but I do have a gift for overlooking the obvious. Your help will be gratefully appreciated.
Bill
All you need to do is something like the below:
Object value = mMBSC.getAttribute(objectName, attributeName);
Or create a proxy object that gets an instance of the MBean interface and allows you to access it that way. A tutorial on how to do this is here: http://docs.oracle.com/javase/tutorial/jmx/remote/custom.html
One note, this is assuming a remote connection, but from your question it seems your are accessing the beans locally? If that is the case then you can use platform.getMBeanServer() to get access to the MBeanServer more directly. E.g. MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
Related
I'm implementing a client to a web service (and the guys maintaining the web service have been a litte unresponsive..) I've used axis and WSDL2Java to generate java classes and I can call their login-method on their authentication-service ok, and get a sessionId back (eg z4zojhiqkw40lj55kgtn1oya). However, it seems that i cannot use this sessionId as a parameter anywhere. Even a call to their hasSession()-method directly after login returned false. I managed to solve this by setting setMaintainSession(true) on the Locator-object for this service. But the problem is, that this first service, the Authentication-service, is only used for authentification. If I then call setMaintainSession(true) on eg ProductServiceLocator, and call some method on it, I will get an error because of unauthenticated session. I have to find a way to share the session between the services on the client side.
Looking on their php code example-it seeems like they are storing the session in a cookie. How can I mimic this behaviour in my java client?
php-code:
$authentication = new SoapClient ( "https://webservices.24sevenoffice.com/authenticate/authenticate.asmx?wsdl", $options );
// log into 24SevenOffice if we don't have any active session. No point doing this more than once.
$login = true;
if (!empty($_SESSION['ASP.NET_SessionId'])){
$authentication->__setCookie("ASP.NET_SessionId", $_SESSION['ASP.NET_SessionId']);
try{
$login = !($authentication->HasSession()->HasSessionResult);
}
catch ( SoapFault $fault ) {
$login = true;
}
}
if( $login ){
$result = ($temp = $authentication->Login($params));
// set the session id for next time we call this page
$_SESSION['ASP.NET_SessionId'] = $result->LoginResult;
// each seperate webservice need the cookie set
$authentication->__setCookie("ASP.NET_SessionId", $_SESSION['ASP.NET_SessionId']);
// throw an error if the login is unsuccessful
if($authentication->HasSession()->HasSessionResult == false)
throw new SoapFault("0", "Invalid credential information.");
}
My code is the following:
AuthenticateLocator al = new AuthenticateLocator();
al.setMaintainSession(true);
Credential c = new Credential(CredentialType.Community,username,password,guid);
AuthenticateSoap s = al.getAuthenticateSoap();
String sessionId = s.login(c);
System.out.println("Session id was: "+sessionId);
System.out.println("Has Session: "+s.hasSession()); //Hooray, now works after setMaintainSession(true)
//And now trying to call another Service
CompanyServiceLocator cl = new CompanyServiceLocator();
cl.setMaintainSession(true);
CompanyServiceSoap css = cl.getCompanyServiceSoap();
css.getCountryList(); //FAILS!
So what can I do to make this work?
Hooray, I finally solved it myself :-D
Thanx a lot to the excellent article at http://www.nsftools.com/stubby/ApacheAxisClientTips.htm
I had to do the following with my code to make it work:
CompanyServiceLocator cl = new CompanyServiceLocator();
cl.setMaintainSession(true);
CompanyServiceSoap css = cl.getCompanyServiceSoap();
((Stub)css)._setProperty(HTTPConstants.HEADER_COOKIE, "ASP.NET_SessionId="+sessionId); //New line that does the magic
css.getCountryList(); //SUCCESS :-D
Operating in the high-level abstraction of the autogenerated classes, it was unknown to me that casting the service classes to Stub would expose more methods and properties that could be set. Good to know for later I guess :-)
I find two ways to get all GarbageCollectorMXBeans from JVM,
First
ManagementFactory.getGarbageCollectorMXBeans() which described from JAVA API
Second
List<GarbageCollectorMXBean> beans = new ArrayList<GarbageCollectorMXBean>();
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName gcName = new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
for (ObjectName name : server.queryNames(gcName, null))
{
GarbageCollectorMXBean gc = ManagementFactory.newPlatformMXBeanProxy(server, name.getCanonicalName(), GarbageCollectorMXBean.class);
beans.add(gc);
}
I want to know any differences between these two?
There is no difference. The first variant is just a shortcut for the second one.
i have a lot of properties so i wanted to reuse as much as possible.
Therefore i have defined generic object :
db-user-generic{
db.host = mymachine
#mssql or oracle
db.type = oracle
db.username = user
db.password= password
mssql {
port = 1433
name = None
url = "jdbc:sqlserver://"${?host}":"${?port}
}
oracle {
port = 1521
serviceNameOrSID = ORCL
url = "jdbc:oracle:thin:#"${?host}":"${?port}":"${?serviceNameOrSID}
}
}
the problem with this code is for example :
"jdbc:oracle:thin:#"${?host}":"${?port}":"${?serviceNameOrSID}
port is not defined for this but db-user-generic.mssql.port is defined.
so now the inned property should know what it is the exact location , which means it could not be reused like this :
my-db= ${db-user-generic}
my-db= {
mssql {
port= 1111
}
}
since the url attribute should be overriden too because he reference to db-user-generic.mssql.port and not to my-db.mssql.port
anyone has idea how this could be resolved?
I'm trying to run:
Map<String, String> environmentProperties = new HashMap<String, String>();
environmentProperties.put("java.naming.security.authentication", "simple");
environmentProperties.put("java.naming.ldap.attributes.binary", "tokenGroups objectSid");
LdapContextSource contextSource = new LdapContextSource();
contextSource.setAnonymousReadOnly(false);
contextSource.setPooled(false);
contextSource.setUserDn("CN=Administrator,CN=Users,DC=someDomain,DC=com");
contextSource.setPassword("password");
contextSource.setUrls(new String[]{"ldap://url.goes.here"});
contextSource.setBaseEnvironmentProperties(environmentProperties);
contextSource.setDirObjectFactory(null);
contextSource.afterPropertiesSet();
final SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
ContextExecutor contextExecutor = new ContextExecutor() {
public Object executeWithContext(DirContext ctx) throws NamingException {
EventDirContext ectx = (EventDirContext) ctx.lookup("CN=Users,,DC=someDomain,DC=com");
ectx.addNamingListener("", "(cn=*)", searchControls, new LDAPChangeListener());
return null;
}
};
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
ldapTemplate.setIgnorePartialResultException(true);
ldapTemplate.executeReadOnly(contextExecutor);
but, the first message my listener gets is:
javax.naming.OperationNotSupportedException: [LDAP: error code 12 - 00000057: LdapErr: DSID-0C090753, comment: Error processing control, data 0, v1db1 ]; remaining name ''
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3127)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3013)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2820)
at com.sun.jndi.ldap.LdapNamingEnumeration.getNextBatch(LdapNamingEnumeration.java:129)
I also ran this code I found here that's supposed to verify that my AD supports persistent search, and the result was true.
static boolean isPersistentSearchSupported(LdapContext rootContext)
throws NamingException {
SearchResult rootDSE;
NamingEnumeration searchResults;
Attributes attrs;
NamingEnumeration attrEnum;
Attribute attr;
NamingEnumeration values;
String value;
String[] attrNames = { "supportedControl" };
SearchControls searchControls = new SearchControls();
searchControls.setCountLimit(0); // 0 means no limit
searchControls.setReturningAttributes(attrNames);
searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
// search for the rootDSE object
searchResults = rootContext.search("", "(objectClass=*)",
searchControls);
while (searchResults.hasMore()) {
rootDSE = (SearchResult) searchResults.next();
attrs = rootDSE.getAttributes();
attrEnum = attrs.getAll();
while (attrEnum.hasMore()) {
attr = (Attribute) attrEnum.next();
values = attr.getAll();
while (values.hasMore()) {
value = (String) values.next();
if (value.equals("1.2.840.113556.1.4.528"))
return true;
}
}
}
return false;
}
what do I need to do to start getting events from AD?
According to the documentation, the scope can't be Subtree and the search filter must be (objectClass=*) for a persistent search.
UPDATE:
I found this: https://forums.oracle.com/thread/1157474?tstart=0
It basically says that AD does not support this, and that there’s no way I can make the above code work.
However, it does give out 2 different ways of getting such notifications from AD:
Using DirSync- I tried the attached code, and it did not work, but did not continue investigating from the reasons that will be listed in the end of this post.
Using LDAP Notifications (https://forums.oracle.com/message/4698114 )- this code worked, however, it only returns results for created / changed events, it will not notify once an object gets deleted, and there’s no way of getting it with this method since the search filter cannot be changed, because any other filter will not work. So it did not fit my purposes, but maybe someone else finds it useful.
I thought DirSync might be the only solution possible for me, if any.
However, it should be noted that DirSync has the following limitations:
The DirSync control can only be used by a highly privileged account, such as a domain administrator.
The DirSync control can only monitor an entire naming context. You cannot limit the scope of a DirSync search to monitor only a specific subtree, container, or object in a naming context.
I hope this information will help someone else in the future.
I would like to add some additional information on this topic since I have done research on this topic couple years ago.
The NamingListener capability provided by JNDI. If you try to register a NamingListener on any LDAP server, that particularly server must support the 'Persistent Search' extension. The Persistent Search extension has always been in IETF draft stage therefore there's no official RFC# associate with it.
Not a lot of LDAP server support this extension. The last time I researched on this topic was 2008 and the LDAP servers that support persistent search extension were 389 Directory Server, Oracle Internet Directory (OID) and OpenDS (now known as OpenDJ).
http://www-archive.mozilla.org/directory/ietf-docs/draft-smith-psearch-ldap-01.txt
http://www.redhat.com/archives/fedora-directory-users/2008-May/msg00120.html
I've seen things like:
user:password#smtpserver:port
In the past, but I'm not sure if the some library parsed that to build a properties to create a session or is there some sort of accepted format.
While there is a SMTP URL Scheme, I have never seen anyone use it. In practice, most applications provide four separate fields for host, port, user name and password. But if you really need to put those four components into a single string, the example you provided is probably the best-known format for something like this.
Using an URI for specifying a network resource, such as an SMTP server is probably the cloeset thing to a "accepted" format you'd see, an SMTP URI, would be something like smtp://user:host#example.com:port or perhaps just smtp://example.com . You'd use a generic URI parsing library to extract the various components.
There's also an old RFC draft for SMTP URLs
I think that would work.
I would like to add as answer, how I'm using java.net.URI class get information from that URI.
class Demo {
public static void main( String ... args ) throws Exception {
System.out.println( inspect( new URI("smtp://user:port#host:25")));
}
// invoke all the getXyz methods on object and construct
// a string with the result.
private static String inspect( Object o ) throws Exception {
StringBuilder builder = new StringBuilder();
for( Method m : o.getClass().getMethods() ) {
String name = m.getName();
if( name.startsWith("get")) {
builder.append( name )
.append(" = " )
.append( m.invoke( o ) )
.append( "\n" );
}
}
return builder.toString();
}
}
Output
getAuthority = user:port#host:25
getFragment = null
getPath =
getQuery = null
getScheme = smtp
getHost = host
getPort = 25
getUserInfo = user:port
getRawAuthority = user:port#host:25
getRawFragment = null
getRawPath =
getRawQuery = null
getRawSchemeSpecificPart = //user:port#host:25
getRawUserInfo = user:port
getSchemeSpecificPart = //user:port#host:25
getClass = class java.net.URI
Was configuring node app and had a hard time getting it.
smtp://username:password#smtphost:port
The funny part was my password was having an '#' so it was presuming the hostname has started.
The username also had an '#' server but it was good as it was separated by the colon ":"
Hope this helps.
Cheers