How to connect to an mBeanServer in another local process? - java

If you set the "com.sun.management.jmxremote" system property when you start a JVM, you can run jconsole or visualvm and connect to that local mBeanServer. I want to do the same thing they are doing but can't figure out how.
Is there a service URL you can use to identify a locally running JVM?
I know I could do this by setting up a jmxmp or rmi listener on a specific port and then connecting to that port, but I don't want to do that because it means I have to manage the ports and know which port goes to which jvm (we run multiple jvms on the same server).

Thanks to Nicholas and Scott for the post and discussion. Was able to piece this together based on that info and some trial and error.
Sharing so others don't have to do the same leg work. This will attach to a JVM and get its memory usage.
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import com.sun.tools.attach.spi.AttachProvider;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class AttachFun {
public static void main(String[] args) throws Exception {
final AttachProvider attachProvider = AttachProvider.providers().get(0);
VirtualMachineDescriptor descriptor = null;
for (VirtualMachineDescriptor virtualMachineDescriptor : attachProvider.listVirtualMachines()) {
if (pickThisOne(virtualMachineDescriptor)) {
descriptor = virtualMachineDescriptor;
break;
}
}
if (descriptor == null) throw new RuntimeException("You didn't pick one");
final VirtualMachine virtualMachine = attachProvider.attachVirtualMachine(descriptor);
virtualMachine.loadAgent("/System/Library/Frameworks/JavaVM.framework/Home/lib/management-agent.jar", "com.sun.management.jmxremote");
final Object portObject = virtualMachine.getAgentProperties().get("com.sun.management.jmxremote.localConnectorAddress");
final JMXServiceURL target = new JMXServiceURL(portObject + "");
final JMXConnector connector = JMXConnectorFactory.connect(target);
final MBeanServerConnection remote = connector.getMBeanServerConnection();
final ObjectName memory = new ObjectName("java.lang:type=Memory");
CompositeData cd = (CompositeData) remote.getAttribute(memory, "HeapMemoryUsage");
final Long used = (Long) cd.get("used");
System.out.println(used);
}
private static boolean pickThisOne(VirtualMachineDescriptor virtualMachineDescriptor) {
// TODO
return false;
}
}

You need the Attach API. It will locate running [Attach API capable] JVMs on the same host and return meta-data on each as well as enable a JMXConnector. It's basically what you see when you start JConsole and the connect menu displays running JVMs, including Java 1.6+ instances that were not started with any special JMX command line directives.
//Nicholas

Related

Use azure-notificationhubs-java-backend via proxy with authentication

Is there a way to use the azure-notificationhubs-java-backend library behind a corporate proxy with authentication?
I will be using the library under an application server (JBoss 6), so I'd like to avoid the classic Java system properties approach (https.proxyHost, https.proxyPort, etc.), since it affects the whole JVM.
Thanks in advance.
Regards,
Nuno Guerreiro
I managed to solve this problem. I'm posting the solution here, just in case anyone needs it ;).
In my specific case, I use a Windows 8 PC and my proxy requires Windows (NTLM) authentication. The code below uses NTLM integrated authentication, i.e., no username and password need to be explicitly set, since the security credentials of the currently logged-on user will be used.
import com.windowsazure.messaging.*;
import java.util.concurrent.Future;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.HttpHost;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.win.WindowsCredentialsProvider;
import org.apache.http.impl.auth.win.WindowsNTLMSchemeFactory;
import org.apache.http.impl.auth.win.WindowsNegotiateSchemeFactory;
import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
import org.apache.http.impl.client.WinHttpClients;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
public class Test9 {
private static HttpAsyncClientBuilder createAsyncBuilderWithProxy(String proxyHost, int proxyPort) {
if (WinHttpClients.isWinAuthAvailable()) {
final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.BASIC, new BasicSchemeFactory())
.register(AuthSchemes.DIGEST, new DigestSchemeFactory())
.register(AuthSchemes.NTLM, new WindowsNTLMSchemeFactory(null))
.register(AuthSchemes.SPNEGO, new WindowsNegotiateSchemeFactory(null))
.build();
final CredentialsProvider credsProvider = new WindowsCredentialsProvider(new SystemDefaultCredentialsProvider());
return HttpAsyncClientBuilder.create()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultAuthSchemeRegistry(authSchemeRegistry)
.setProxy(new HttpHost(proxyHost, proxyPort));
} else {
return HttpAsyncClientBuilder.create().setProxy(new HttpHost(proxyHost, proxyPort));
}
}
public static void main(String[] args) throws Exception {
if(args.length < 4) {
System.err.println("syntax: java Test9 <hub connection string> <hub name> <push notification address> <push message>");
} else {
String hubConnectionString = args[0];
String hubName = args[1];
String pushNotificationAddress = args[2];
String pushMessage = args[3];
CloseableHttpAsyncClient httpClient = createAsyncBuilderWithProxy("proxy.corporate.com", 8080).build();
httpClient.start();
HttpClientManager.setHttpAsyncClient(httpClient);
NotificationHub hub = new NotificationHub(hubConnectionString, hubName);
Notification notification = Notification.createGcmNotifiation(pushMessage);
hub.sendDirectNotification(notification, pushNotificationAddress);
System.out.println("Notification sent!");
httpClient.close();
}
}
}
Based on the azure-notificationhubs-java-backend library without the proxy setting apis, there seems to be not any way to use it via proxy with authentication without any affection for the whole JVM of your JBoss.
So per my experience, the only way is creating a new instance of JBoss server using Java system properties for proxy settings to run your Azure notificationhubs java backend and communicate with your main application on the other JBoss server instance via the RPC ways like REST API, WS* API, etc.
Hope it helps.

List all the user in weblogic by java

Does anyone know how to list all the weblogic users in java?
For instance, there is 5 users in security realm, and I want to get all of them. How do I do?
It's pretty easy. For future reference, if you want to look up something like "how do I do X with weblogic and Java..." use JMX in your google search. Here is an example from weblogic wonders. Note you will need to change your URL and user/password in the code:
import javax.naming.*;
import javax.management.MBeanInfo;
import weblogic.jndi.Environment;
import weblogic.management.runtime.ServerRuntimeMBean;
import weblogic.security.providers.authentication.DefaultAuthenticatorMBean;
import weblogic.management.security.authentication.UserReaderMBean;
import weblogic.management.security.authentication.GroupReaderMBean;
import weblogic.management.MBeanHome;
import weblogic.management.WebLogicMBean;
import weblogic.management.tools.Info;
import weblogic.management.Helper;
import weblogic.management.security.authentication.*;
public class ListUsersAndGroups
{
public static void main(String[] args)
{
MBeanHome home = null;
try
{
Environment env = new Environment();
env.setProviderUrl(“t3://localhost:7001?);
env.setSecurityPrincipal(“weblogic”);
env.setSecurityCredentials(“weblogic”);
Context ctx = env.getInitialContext();
home = (MBeanHome)ctx.lookup(“weblogic.management.adminhome”);
weblogic.management.security.RealmMBean rmBean =
home.getActiveDomain().getSecurityConfiguration().getDefaultRealm();
AuthenticationProviderMBean[] authenticationBeans =
rmBean.getAuthenticationProviders();
DefaultAuthenticatorMBean defaultAuthenticationMBean =
(DefaultAuthenticatorMBean)authenticationBeans[0];
UserReaderMBean userReaderMBean =
(UserReaderMBean)defaultAuthenticationMBean;
String userCurName = userReaderMBean.listUsers(“*”, 100);
while (userReaderMBean.haveCurrent(userCurName) )
{
String user = userReaderMBean.getCurrentName(userCurName);
System.out.println(“\n User: ” + user);
userReaderMBean.advance(userCurName);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
EDIT
There isn't really any way around have to know the user/password to look up the users. You can do it via WLST scripting as well if that sounds like a better option. See an example here.
Last but not least, you could set anonymous bind on the embedded ldap for Weblogic to allow anonymous lookups (which is generally not recommended for production). This sample shows how to do it with an external client: Weblogic w/External Ldap Client
The key settings are:
Your Domain -> Security -> Embedded LDAP
Change the (default generated) password (for example: weblogic)
Enable “Anonymous Bind Allowed”

How to browse the file system of a server machine when connecting with a client (java)

I'm in the process of making a proof of concept to dissociate the business code from the gui for the ps3 media server (http://www.ps3mediaserver.org/). For this I've got a project hosted at source forge (http://sourceforge.net/projects/pms-remote/). The client should be a simple front end to configure the server from any location within a network having the rights to connect to the server.
On the server side, all service have been exposed using javax.jws and the client proxy has been generated using wsimport.
One of the features of the current features (actually, the only blocking one), is to define the folders that will be shared by the server. As the client and server are now running as a single application on the same machine, it's trivial to browse its file system.
Problem: I'd like to expose the file system of the server machine through web services. This will allow any client (the one I'm currently working on is the same as the original using java swing) to show available folders and to select the ones that will be shown by the media server. In the end the only thing I'm interested in is an absolute folder path (string).
I thought I'd find a library giving me this functionality but couldn't find any.
Browsing the files using a UNC path and accessing a distant machine doesn't seem feasible, as it wouldn't be transparent for the user.
For now I don't want to worry about security issues, I'll figure these out once the rest seems feasible.
I'd be grateful for any input.
Thanks, Philippe
I've ended up creating a pretty simple web service letting either list all root folders or all child folders for a given path.
It's now up to the client to have a (GUI) browser to access this service.
package net.pms.plugin.webservice.filesystem;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import net.pms.plugin.webservice.ServiceBase;
#WebService(serviceName = "FileSystem", targetNamespace = "http://ps3mediaserver.org/filesystem")
public class FileSystemWebService extends ServiceBase {
#WebMethod()
public List<String> getRoots() {
List<String> roots = new ArrayList<String>();
for(File child : File.listRoots()) {
roots.add(child.getAbsolutePath());
}
return roots;
}
#WebMethod()
public List<String> getChildFolders(#WebParam(name="folderPath") String folderPath) throws FileNotFoundException {
List<String> children = new ArrayList<String>();
File d = new File(folderPath);
if(d.isDirectory()) {
for(File child : d.listFiles()) {
if(child.isDirectory() && !child.isHidden()) {
children.add(child.getAbsolutePath());
}
}
} else {
throw new FileNotFoundException();
}
return children;
}
}
For people wanting to use this, here's the ServiceBase class as well
package net.pms.plugin.webservice;
import javax.xml.ws.Endpoint;
import org.apache.log4j.Logger;
public abstract class ServiceBase {
private static final Logger log = Logger.getLogger(ServiceBase.class);
protected boolean isInitialized;
/**
* the published endpoint
*/
private Endpoint endpoint = null;
/**
*
* Start to listen for remote requests
*
* #param host ip or host name
* #param port port to use
* #param path name of the web service
*/
public void bind(String host, int port, String path) {
String endpointURL = "http://" + host + ":" + port + "/" + path;
try {
endpoint = Endpoint.publish(endpointURL, this);
isInitialized = true;
log.info("Sucessfully bound enpoint: " + endpointURL);
} catch (Exception e) {
log.error("Failed to bind enpoint: " + endpointURL, e);
}
}
/**
* Stop the webservice
*/
public void shutdown() {
log.info("Shut down " + getClass().getName());
if (endpoint != null)
endpoint.stop();
endpoint = null;
}
}
From the client, you might be able to leverage the output of smbclient -L. On the server, a suitable servlet might do.

Why is inet:/0.0.0.0 interpreted as inet:/0:0:0:0:0:0:0:0 by com.sun.net.httpserver.HttpServer?

We found this issue while implementing a reconnect logic for a WS endpoint using JAX-WS stack deployed on Glassfishv2.1 server. We deploy the web service on a cluster environment. To simplify the deployment, we use 0.0.0.0 as the IP on which the endpoint needs to be published, so that it can be accessed from all available IPs pertaining to the nodes of the cluster. Following is the code snippet for initialization of the WS(Web Service):
import javax.xml.ws.Endpoint;
.
.
//Implementor is the corresponding implementation object for the WS
Endpoint receiver = Endpoint.create(new Implementor());
.
receiver.setExecutor(threadPoolExecutor);
receiver.publish ("http://0.0.0.0:9545/context");
We call receiver.stop() to stop publishing the endpoint in our cleanup code. That's where we receive a null pointer exception with the following stack trace:
java.lang.NullPointerException
at com.sun.xml.ws.transport.http.server.ServerMgr.removeContext(ServerMgr.java:123)
at com.sun.xml.ws.transport.http.server.HttpEndpoint.stop(HttpEndpoint.java:110)
at com.sun.xml.ws.transport.http.server.EndpointImpl.stop(EndpointImpl.java:167
While trying to find the cause of the NPE, we found that the ServerMgr class depends on the InetSocketAddress of the HttpServer that listen on the ip:port of the URL where the WS endpoint is published, to retrieve some state information from a map. Since the inet address "inet:/0.0.0.0" is interpreted as "inet:/0:0:0:0:0:0:0:0" it could not find the entry in the map and hence the NPE. Here is the source code of ServerMgr.
In order to prove that this is in fact the problem, we tried to replicate the logic of the ServerMgr code related to InetSocketAddress as the following program:
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
static final String URL_1 = "http://0.0.0.0:9545/context";
static final String URL_2 = "http://127.0.0.1:9548/context";
static final String URL_3 = "http://10.226.90.217:9549/context";
public void testUrl(String address){
try {
URL url = new URL(address);
Map<InetSocketAddress, Integer> map = new HashMap<InetSocketAddress, Integer>();
InetSocketAddress iaddr = new InetSocketAddress(url.getHost(), url.getPort());
map.put(iaddr, 1);
HttpServer server = HttpServer.create(iaddr, 5);
HttpContext context = server.createContext(url.toURI().getPath());
server.start();
System.out.println("original inet:"+iaddr+" and final inet:"+context.getServer().getAddress());
if(iaddr.equals(context.getServer().getAddress())){
System.out.println("equal");
Integer t = map.get(context.getServer().getAddress());
if( t == null){
System.out.println("You won");
}else{
System.out.println("You lose "+t);
}
}else{
System.out.println("not-equal");
}
server.stop(0);
map.clear();
} catch (URISyntaxException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args)
{
Main d = new Main();
d.testUrl(Main.URL_1);
d.testUrl(Main.URL_2);
d.testUrl(Main.URL_3);
}
}
Odd enough we obtain the following result in my WindowsXP box ( Java version 1.6.0_22)
equal--
original inet:/0.0.0.0:9545 and final inet:/0.0.0.0:9545
equal
You lose 1
equal--
original inet:/127.0.0.1:9548 and final inet:/127.0.0.1:9548
equal
You lose 1
equal--
original inet:/10.226.92.47:9549 and final inet:/10.226.92.47:9549
equal
You lose 1
and the following output on my dev box
(Linux tahoe 2.6.9-67.EL #1 Wed Nov 7 13:43:31 EST 2007 x86_64 x86_64 x86_64 GNU/Linux)
(Java version 1.6.0_17)
run:
original inet:/0.0.0.0:9545 and final inet:/0:0:0:0:0:0:0:0:9545
not-equal
original inet:/127.0.0.1:9548 and final inet:/127.0.0.1:9548
equal
You lose 1
original inet:/10.226.90.217:9549 and final inet:/10.226.90.217:9549
equal
You lose 1
Based on the background - I have two questions:
a. Why is the 0.0.0.0 is interpreted
as IPv6 address ? (In addition, is it a problem
with the OS or the JRE ? Is it a bug
or a feature ? etc)
b. Do we have a way to configure
JRE to interpret 0.0.0.0 as IPv4 address ?
(We want to keep using 0.0.0.0 as the endpoint
address as it simplifies deployment
of our Web Service)
You can replace the 0.0.0.0 with InetAddress.getLocalHost().getHostAddress();
It will automatically resolve correctly and there is no need of -Djava.net.preferIPv4Stack=true
Have you tried by forcing the IPv4 choice as a variable to the java executable?
-Djava.net.preferIPv4Stack=true
Take a look here and here..
It's also possible to force IPv4 from your code with System.setProperty("java.net.preferIPv4Stack" , "true");

Determine if Java App is being run over an RDP Session?

How can I detect if my Swing App is being run from a windows RDP session?
Java only solution preferred, but the app is guaranteed to be running on windows so I'm ok with shelling out.
I think you'll have to invoke the native Windows libraries to pull this off. Try something like this:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.*;
import com.sun.jna.examples.win32.Kernel32;
...
public static boolean isLocalSession() {
Kernel32 kernel32;
IntByReference pSessionId;
int consoleSessionId;
Kernel32 lib = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
pSessionId = new IntByReference();
if (lib.ProcessIdToSessionId(lib.GetCurrentProcessId(), pSessionId)) {
consoleSessionId = lib.WTSGetActiveConsoleSessionId();
return (consoleSessionId != 0xFFFFFFFF && consoleSessionId == pSessionId.getValue());
} else return false;
}
That strange-looking condition for consoleSessionId is from the documentation for WTSGetActiveConsoleSessionId, which says:
Return Value
The session identifier of the session that is attached to the physical console. If there is no session attached to the physical console, (for example, if the physical console session is in the process of being attached or detached), this function returns 0xFFFFFFFF.
The above answers might work, but seem needlessly complicated. You can simply read the windows 'sessionname' environment variable to detect RDP sessions. The value of this environment variable will be 'Console' for a normal, local session. For an RDP session it will contain the phrase 'RDP'. It's easy enough just to check for that.
public static boolean isRemoteDesktopSession() {
System.getenv("sessionname").contains("RDP");
}
Tested and confirmed working under Windows7 64bit. One issue I have noticed with this technique is that it appears that environment variable values as read from System.getenv() do not change once the JVM has started. So if the JVM process was started by a console session, but then accessed by an RDP session, further calls to System.getenv("sessionname") still return 'Console.'
Try with NativeCall ( http://johannburkard.de/software/nativecall/ )
All you need is 2 jars plus 1 DLL in your classpath.
A quick test :
import java.io.IOException;
import com.eaio.nativecall.*;
public class WindowsUtils {
public static final int SM_REMOTESESSION = 4096; // remote session
public static boolean isRemote() throws SecurityException, UnsatisfiedLinkError,
UnsupportedOperationException, IOException
{
NativeCall.init();
IntCall ic = null;
ic = new IntCall("user32", "GetSystemMetrics");
int rc = ic.executeCall(new Integer(SM_REMOTESESSION));
if (ic != null) ic.destroy();
return (rc > 0);
}
public static void main(String ... args) throws Exception {
System.out.println(WindowsUtils.isRemote());
}
}

Categories

Resources