j2se|web| Security
first, the introduction
JMX (Java Management Extensions) provides a set of tools for managing local and remote applications, system objects, devices, and so on. This article explains how to use JMX (JSR 160) to remotely control Web applications, and will interpret code that can be used in applications for JMX customers, while demonstrating how different customers, such as mc4j and jmanage, connect to JMX-enabled applications. In addition, we will discuss in detail the use of RMI protocol and Jndi to protect the communication layer.
First we want to analyze a simple Web application that monitors the number of users who have logged in and displays the statistics through a secure JMX service. We will also run multiple instances of the application and track the statistics from all the running instances. Of course, you can download this sample Web application. It requires you to install the J2SE 5.0 SDK and your JAVA_HOME environment variable points to the base installation directory. The J2SE 5.0 implements the 1.2 version of the JMX API and the JMX 1.0 version of the remote API. You also need a container that supports the servlet; I'm using Apache Tomcat 5.5.12. Also, I use Apache ant to build this sample application.
second, the establishment of the sample application
First, you will download the sample application and use the Ant War (see the annotations in Build.xml for more details) to create a war file. Copy the Jmxapp.war to Tomcat's WebApps directory. Assuming Tomcat is running on port 8080 on your local machine, the URL for the application will be:
Http://localhost:8080/jmxapp
If you see a landing screen that prompts you to enter a name and password, everything is ready.
third, tracking some meaningful data
The application in this article uses the Struts framework to submit a login form. Once the commit is complete, the execution Loginaction.execute (..) Method-it will simply check whether the user's ID is "Hello" and whether the password is "world". If both are correct, the login succeeds and the control is directed to the login_success.jsp; if not, then we return to the login form. Decide whether to call the Incrementsuccesslogins (HttpServletRequest) method or the Incrementfailedlogins (HttpServletRequest) method based on the success of the login. Now, let's analyze Incrementfailedlogins (httpservletrequest) First:
private void Incrementfailedlogins (HttpServletRequest request) {
HttpSession session = Request.getsession ();
ServletContext context =session.getservletcontext ();
Integer num = (integer) context.getattribute (Constants.failed_logins_key);
int newvalue = 1;
if (num!= null) {newvalue = Num.intvalue () + 1;}
Context.setattribute (Constants.failed_logins_key, New Integer (NewValue));
}
This method adds a failed_logins_key variable stored in the application scope. This incrementsuccesslogins (HttpServletRequest) method is implemented in a similar way. The application tracks how many people have successfully logged in and how many people have failed to authenticate. That's nice, but how do we access that data? This is the reason for the introduction of JMX.
Iv. Creating a jmx Mbeans
Mbeans basic knowledge and its suitability for JMX architecture are beyond the scope of this article. We will simply create, implement, expose, and protect an mbean for our applications. We are interested in exposing two types of data corresponding to the following two methods. Here is our simple Mbean interface:
Public interface Loginstatsmbean {
public int getfailedlogins ();
public int getsuccesslogins ();
}
These two methods simply return the number of successful and unsuccessful landings. The realization of Loginstatsmbean is-loginstats, which provides a concrete implementation for the above two methods. Let's analyze the Getfailedlogins () implementation:
public int Getfailedlogins () {
ServletContext context = Config.getservletcontext ();
Integer val = (integer) context.getattribute (Constants.failed_logins_key);
return (val = = null)? 0:val.intvalue ();
}
This method returns a value stored in the ServletContext. The Getsuccesslogins () method is implemented in a similar way.
v. Create and protect a JMX agent
The Jmxagent class that manages the JMX-related aspects of an application has the following responsibilities:
1. Create a mbeanserver.
2. Register Loginstatsmbean with Mbeanserver.
3. Create a jmxconnector to allow remote clients to connect.
O contains the use of Jndi.
O also must have an RMI registration run.
4. Use a username and password to protect Jmxconnector.
5. Start and stop Jmxconnector, respectively, when the application starts and stops.
The Jmxagent class outlines are:
public class Jmxagent {
Public Jmxagent () {
Initializing a JMX server
}
public void Start () {
Start JMX Server
}
Called at the end of the application
public void Stop () {
Stop JMX Server
}
}
Let's understand this part of the code in the constructor-it enables customers to monitor the application remotely.
Create a mbeanserver with Mbeans
We first create a Mbeanserver object. It is the core component of the JMX infrastructure, which allows us to expose our Mbeans as manageable objects. The Mbeanserverfactory.creatembeanserver (String) approach makes this task extremely easy. The supplied parameter is the domain of the server. You can think of it as the only name for this mbeanserver. Then, we use Mbeanserve to register Loginstatsmbean. The Mbeanserver.registermbean (Object,objectname) method uses two parameters: one is an instance of an Mbean implementation, and the other is an object of type objectname-it is used to uniquely identify the Mbean In this case, domain+ ": Name=loginstats" is enough.
Mbeanserver Server = Mbeanserverfactory.creatembeanserver (DOMAIN);
Server.registermbean (New Loginstats (), New ObjectName (domain+ ": Name=loginstats"));
Vi. creation of Jmxserviceurl
So far, we have created a mbeanserver and used it to register Loginstatsmbean. The next step is to make the server available to customers. To do this, we must create a jmxserviceurl-that describes the URL that the customer will use to access the JMX service:
Jmxserviceurl url = new Jmxserviceurl ("RMI", NULL,
Constants.mbean_server_port,
"/jndi/rmi://localhost:" +constants.rmi_registry_port + "/jmxapp");
Let's look at the above line of code in detail. The Jmxserviceurl constructor uses four parameters:
1. Protocols used at the time of connection (RMI,JMXMP,IIOP, etc.).
2. JMX Service Host. Using localhost as a parameter is enough. However, it is possible to provide null force Jmxserviceurl to find the best host name. For example, in this case, it will translate null into zarar-this is the name of my computer.
3. The port used by the JMX service.
4. Finally, we must provide a URL path-it indicates how to find the JMX service. In this case, it would be/jndi/rmi://localhost:1099/jmxapp.
Among them, the/jndi section means that the customer must do a jndi lookup for the JMX service. rmi://localhost:1099 indicates that there is an RMI registration running on port 1099 on this computer. The Jmxapp here is the only way to identify this JMX service in RMI registration. A ToString () on the Jmxserviceurl object produces the following results:
Service:jmx:rmi://zarar:9589/jndi/rmi://localhost:1100/jmxapp
Above is the URL that the customer will eventually use to connect to the JMX service. The J2SE 5.0 document has a more detailed explanation of this URL structure.
(i) Protection services
J2SE 5.0 provides a mechanism to help JMX authenticate users in an easy way. I created a simple text file-it stores username and password information. The contents of the document are:
Zarar Siddiqi
Fyodor Dostoevsky
User Zarar and Fyodor are authenticated by password Siddiqi and Dostoevsky respectively. The next step is to create and protect a jmxconnectorserver that exposes the mbeanserver. The path to the Username/password file is stored in a map under the key-jmx.remote.x.password.file. This mapping is used when creating jmxconnectorserver later.
ServletContext context = Config.getservletcontext ();
Get files that store JMX user information
String userfile =context.getrealpath ("/") + "/web-inf/classes/" +constants.jmx_users_file;
Create authenticator and initialize RMI server
map<string> env = new hashmap<string> ();
Env.put ("Jmx.remote.x.password.file", userfile);
Now, let's create the Jmxconnectorserver. The following line of code completes this function:
Connectorserver = Jmxconnectorserverfactory.
Newjmxconnectorserver (URL, env, server);
This jmxconnectorserverfactory.newjmxconnectorserver (jmxserviceurl,map,mbeanserver) Method uses the three objects we have just created as parameters-they are jmxserviceurl, which stores the mappings and mbeanserver of the authentication information. Where the Connectorserver instance variable allows us to start and stop the Jmxconnectorserver separately when the application starts and stops, respectively, with start () and stop ().
Tip Although the J2SE 5.0 implementation of JSR 160 is fairly powerful, other implementations, such as MX4J, also provide classes-they provide convenient features such as password obfuscation, or Passwordauthenticator classes.
Vii. Launch RMI registration
Earlier, I mentioned RMI registration and pointed out that executing a JNDI query when accessing the service. However, now we do not have a running RMI registration, so a jndi query will fail. A RMI registration boot can be implemented either manually or programmatically.
(i) using the command line
On your Windows or Linux command line, enter one of the following to start an RMI registration:
Rmiregistry &
This will start the RMI registration of your default host and port (respectively localhost and 1109). However, for our web applications, it is not possible to rely on a RMI that is available when the application is started, rather than in a programmatic way.
(ii) Programmatic launch of RMI registration
In order to start RMI registration programmatically, you can use the locateregistry.createregistry (int port) method. This method returns an object of type registration. We save this reference when we want to terminate this registration at the end of the application. Just before we start our jmxconnectorserver in Jmxagent.start (), we start RMI registration and use the following line of code:
Registry = Locateregistry.createregistry (Constants.rmi_registry_port);
At the end of the application, after stopping the Jmxconnectorserver in Jmxagent.stop (), call the following methods to terminate the registration:
Unicastremoteobject.unexportobject (registry,true);
Note that the Startuplistener class triggers the application start and end tasks.
Eight, visit our JMX service
There are several ways we can access the JSR 160 service. To do this, we can do this by programming or by using a GUI.
(i) Use of mc4j connection
Publish the application by copying the Jmxapp.war to Tomcat's WebApps directory. Download and install mc4j. Once installed, create a new type of JSR 160 server connection and specify the server url-it prints in the application server log when the application starts. In my example, it is:
Service:jmx:rmi://zarar:9589/jndi/rmi://localhost:1100/jmxapp
Provide username and password, mc4j refer to them as "principle" and "Credentials" respectively. Clicking Next will take you to a screen-where you can customize your classpath. The default setting should work properly, and you can click Finish to connect to the JMX service. Once the connection is established, browse to the MC4J tree structure as shown in Figure 1 until you find the "Properties" option that Loginstats Mbean implements.
Figure 1. MC4J View
Click Properties to display statistics, as shown in Figure 2:
Figure 2. Properties Window
(ii) using Jmanage to connect to a "cluster"
Publish the application by copying the Jmxapp.war to Tomcat's WebApps directory. Note The URL that is printed when the application starts. Next, publish another instance of the application-by changing the Rmi_registry_port in the constants class and mbean_server_port the variable, the second instance of the application will not attempt to use the port already in use. Change the App.name property in the Build.xml file so that the new instance will be published to a different context (for example, JMXAPP2). Create a clean war file with Ant-it will create the Jmxapp2.war in its directory. Copy the Jmxapp2.war to Tomcat's WebApps directory. The application will be published, and now you have two instances of the same application running. Again, I remind you to note the URL that was printed at startup.
Download and install Jmanage. Once installed, use the Jmanage Web interface to create a JSR 160 application-by using the "Add New Application" link in the home page. The Add Application page appears in Figure 3:
Figure 3. " Add Application Page
Repeat the previous steps for the second application you want to publish and use the appropriate username, password, and URL again ... Once you have created these two applications, you must create a cluster by following the "Add New application Cluster" link found in the home page. Now add the two already created applications to your cluster, as shown in Figure 4:
Figure 4. Add Application Cluster page
Well, we've done it! From the home page, click on one of the apps in the cluster and click the Find more Objects button. You will see the Name=loginstats MBean; Click on it, and you will see the failedlogins and Successlogins attributes we have exposed. Clicking on the "Cluster View" link on the same page will display a page similar to Figure 5-where you can see the run count statistics for two applications:
Figure 5. Cluster view for Jmxapp and JMXAPP2
Try logging into two applications (Http://localhost:8080/jmxapp and HTTP://LOCALHOST:8080/JMXAPP2) and see how these numbers change.
Ix. Conclusion
Now you know how to get your new and existing Web applications to support JMX and manage them securely-using mc4j and Jmanage. While J2SE 5.0 provides a powerful implementation of the JMX specification, additional open-source projects such as Xmojo and mx4j provide additional features, such as connections through web interfaces or even more. If interested readers want to know more about JMX, you can look at J. The book "Java Management Extensions" written by Steven Perry. If you are interested in remote application management, Jeff Hanson's "Connecting JMX Customer and Servers" will be read-worthy, providing a lot of real-world examples.