|
Welcome to the Core Java Technologies Tech Tips for March 15, 2005. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).
This issue covers:
Generics
JMX, JConsole, and You
These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp.
This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc.
See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.
For more Java technology content, visit these sites:
java.sun.com - The Java technology source for developers. Get the latest Java platform releases, tutorials, newsletters and more.
java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together.
java.com - The ultimate marketplace promoting Java technology, applications and services.
GENERICS
The 5.0 release of J2SE includes the first set of significant language-level changes to the Java platform in some time. In addition to new constructs for things such as the enhance for loop and variable argument lists, J2SE 5.0 provides compile-time type safety with the Java Collections framework through generics in accordance with JSR-14: Add Generic Types To The Java Programming Language.
One of the primary uses of generics is to abstract data types when working with collections. Prior to the JDK 5.0 release, when you created a Collection, you could put anything in it, for example:
List myList = new ArrayList(10);
myList.add(new Integer(10));
myList.add("Hello, World");
If you wanted to restrict your Collection to a specific type, it was difficult at best. Getting items out of the collection required you to use a casting operation:
Integer myInt = (Integer)myList.iterator().next();
If you accidently cast the wrong type, the program would successfully compile, but an exception would be thrown at runtime. Unless you dealt specifically with everything in the Collection as an Object, casting typically happened blindly -- or by doing an instanceof check before calling the casted operation.
Iterator listItor = myList.iterator();
Object myObject = listItor.next();
Integer myInt = null;
if (myObject instanceof Integer) {
myInt = (Integer)myObject;
}
That situation underscores the beauty of generics. Generics allows you to specify, at compile-time, the types of objects you want to store in a Collection. Then when you add and get items from the list, the list already knows what types of objects are supposed to be acted on. So you don't need to cast anything. The "<>" characters are used to designate what type is to be stored. If the wrong type of data is provided, a compile-time exception is thrown. For example, if you try to compile the following class:
import java.util.*;
public class First {
public static void main(String args[]) {
List<Integer> myList = new ArrayList<Integer>(10);
myList.add(10);
myList.add("Hello, World");
}
}
you get an error like this:
First.java:7: cannot find symbol
symbol : method add(java.lang.String)
location: interface java.util.List<java.lang.Integer>
myList.add("Hello, World");
^
1 error
This message basically says that there is no add(String) method available when the interface is for a List of Integer objects. The myList.add(10); method did add the Integer object of number 10 to the list. Autoboxing converted the int type to an Integer. It is only the adding of a String to a List of Integer objects that failed here.
If you work with elements of a collection and don't explicitly state the type of elements in that collection, you're warned about that, too. For instance, the following program creates a List, but it doesn't specify what type is to be stored in the List:
import java.util.*;
public class Second {
public static void main(String args[]) {
List list = new ArrayList();
list.add(10);
}
}
When you compile the program, you get the following warning:
Note: Second.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Yes, that is a warning. If you then follow the directions and add -Xlint:unchecked to the javac command line:
javac -Xlint:unchecked Second.java
you will see the following added information:
Second.java:6: warning: [unchecked] unchecked call to add(E)
as a member of the raw type java.util.List
list.add(10);
^
The message means that the type of element added to the List, the "E" in the "add(E)" call, was never specified. The message is just a warning though, so a .class file was created.
If you want to get rid of the warning, you can add <Object> to the List declaration, as follows:
List<Object> list = new ArrayList<Object>();
Here, Object is the E, and basically says that anything can be stored in the List.
Where does E come from though? If you look at the interface declaration for java.util.List, you see the following:
public interface List<E> extends Collection<E>
This literally says that it is declaring a List of E's.
Later in the interface definition, you see methods where the argument or return type is replaced by an E:
Iterator<E" iterator();
ListIterator<E" listIterator();
boolean add(E o);
boolean addAll(Collection<? extends E" c);
Because the E is specified at compile time, you don't have to rely on runtime exceptions to find type mismatches. The ? in the addAll line can be thought of as a collection of unknown, but the unknown would be E or a subclass. This construct allows you to work with collections of subclasses, and not simply exact matches.
Although not fully explored in the first example, generics is commonly used with the enhanced for loop, with an iterator. Without generics, looping through a List of String objects requires you to cast each element retrieved from the List to the appropriate type. However things get simpler with generics and the enhanced for loop.
Here's the old way of looping through a List of String objects, that is, without generics and an enhanced for loop:
import java.util.*;
public class Old {
public static void main(String args[]) {
List list = Arrays.asList(args);
Iterator itor = list.iterator();
while (itor.hasNext()) {
String element = (String)itor.next();
System.out.println(element + " / " + element.length());
}
}
}
If you compile and run the Old class and then run it with a string, like this:
java Old Hello
you get:
Hello / 5
With JDK 5.0, you can combine the new enhanced for loop construct with generics to create a program that is type-safe at compile-time, and is more readable and more maintainable. Here's what looping through a List of String objects looks like if you use generics and an enhanced for loop:
import java.util.*;
public class New {
public static void main(String args[]) {
List<String> list = Arrays.asList(args);
for (String element : list) {
System.out.println(element + " / " + element.length());
}
}
}
java New Hello
Hello / 5
As demonstrated here, generics and the enhanced for loop work well together.
For more information on generics, see:
JMX, JCONSOLE, AND YOU
JSR-160 defines an API that extends the Java Management Extensions (JMX) version 1.2 APIs to provide remote access to JMX MBean servers. If you recall from the January 22, 2003 Tech Tip, Getting Started with the Java Management Extensions (JMX), you can carefully define components that can be monitored and managed through the JMX APIs. This monitoring and management capability is now a standard part of J2SE 5.0, and with JSR-160, there's now improved remote handling. For additional information on getting started with JMX, see the recent Enterprise Java Technologies Tech Tip from Robert Eckstein, titled Understanding JMX Technology.
In this tip, you'll take the code example in the earlier Getting Started with JMX tip, and update it for J2SE 5.0. You'll also learn how to use the new jconsole tool to display statistics for the running program.
In the earlier "Getting Started with JMX" tip, you needed to explicitly add JAR files to your classpath to compile and run the example program. However if you work with J2SE 5.0, you no longer have to explicitly add the JAR files to your classpath. That's because JMX is now a standard part of J2SE 5.0. All you need to do is make a few changes to the original source, and the program will compile and run with a standard Java development platform.
The MBean class in the earlier tip (HelloMBean) was packageless. It is always best to place your components in packages. To do that, create a directory for your source and add a package statement for that directory structure:
package bean;
public interface HelloMBean {
public String getMessage();
public void setMessage(String message);
public int getChangeCount();
public void resetCounter();
}
Similarly, the only change to the MBean implementation (Hello) is to add the package statement:
package bean;
public class Hello implements HelloMBean {
private String message;
private int changeCount;
public String getMessage() {
return message;
}
public void setMessage(String message){
this.message = message;
changeCount++;
}
public int getChangeCount() {
return changeCount;
}
public void resetCounter() {
changeCount = 0;
}
}
The place where the more significant changes need to be made is in the agent (HelloAgent) that creates the component and registers it with the MBean server. You need to add the package statement here too. Also, you need to slightly change the way of getting the MBeanServer. J2SE 5.0 introduces the platform MBeanServer which is designed for use by application MBeans as well as platform MBeans. It can serve as a single point of network publishing. It's recommended that applications use the platform MBeanServer instead of creating their own. The old way of getting the MBeanServer required importing the javax.management package and creating the server with a call to the createMBeanServer method of MBeanServerFactory:
MBeanServer server =
MBeanServerFactory.createMBeanServer();
The new approach essentially does the same thing, but it allows all management resources to register in the single JMX agent (platform MBeanServer) instead of in multiple different ones. To get the platform MBeanServer, you need to call ManagementFactory.getPlatformMBeanServer and the ManagementFactory class is in java.lang.management package:
MBeanServer server =
ManagementFactory.getPlatformMBeanServer();
Here's the updated version of HelloAgent:
package bean;
import java.lang.management.*;
import javax.management.*;
public class HelloAgent {
public static void main(String args[]) {
MBeanServer server =
ManagementFactory.getPlatformMBeanServer();
HelloMBean hello1 = new Hello();
HelloMBean hello2 = new Hello();
try {
ObjectName helloObjectName1 = new ObjectName(
"bean:type=Hello,name=hello1");
server.registerMBean(hello1, helloObjectName1);
ObjectName helloObjectName2 = new ObjectName(
"bean:type=Hello,name=hello2");
server.registerMBean(hello2, helloObjectName2);
System.out.println("Waiting...");
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
System.out.println("Sleep stopped");
e.printStackTrace();
} catch (MalformedObjectNameException e) {
System.out.println("Bad object name");
e.printStackTrace();
} catch (InstanceAlreadyExistsException e) {
System.out.println("Already exists");
e.printStackTrace();
} catch (MBeanRegistrationException e) {
System.out.println("Registration problems");
e.printStackTrace();
} catch (NotCompliantMBeanException e) {
System.out.println("Registration problems");
e.printStackTrace();
}
}
}
Now, let's look at the jconsole tool. JDK 5.0 includes a new Swing-based tool called jconsole that attaches to a running Java program and displays statistics about the program's performance, memory usage, and thread stack traces, as well as other MBean attributes and operations. One point worth mentioning is that with jconsole, you no longer need to create an HtmlAdaptorServer.
To use jconsole with the updated example program, you need to compile the three classes (HelloMBean, Hello, and HelloAgent) and then execute them with an appropriate command-line switch. When you compile the three classes, be sure to remember that they are in a package.
You can use the following command in the Windows environment to compile the classes. Run the command from the parent directory of the location where you created the classes, and replace "bean" with the actual name of location where you placed the files.
javac bean\*.java
This places the .class files in the same directory as the source files. If you want them in a different directory, use the -d option (but then you'll need to make sure the new location is in your classpath).
In the Solaris operating system or in a Linux or Mac environment, simply turn the slash around:
javac bean/*.java
To execute a JMX-enabled program so that it can be monitored, you need to set the com.sun.management.jmxremote command line property:
java -Dcom.sun.management.jmxremote HelloAgent
Because of the Thread.sleep() call at end of the main() method, the method won't return immediately. At this point, you should start the jconsole program on the same host where the JMX agent is running. Before doing that, make sure the HelloAgent program is running. To start jconsole, you have two options. You can either discover the process id (PID) of the agent program with the new jps command-line tool, and pass that as a command-line option, or simply start jconsole without any options and it will find your program. To start jconsole without specifying a PID, enter the following command:
jconsole
When started with no PID, the first thing you'll see is a window labeled "JConsole: Connect to Agent". If HelloAgent is running, you'll see it in the list.
Select the agent and press Connect. This takes you to the initial Summary screen.
The Memory, Threads, and Classes tabs have summary information on this first screen. You can also select these tabs to see additional information.
The Memory tab allows you to chart heap memory usage and memory usage of each memory pool. You can also request a garbage collection call and see garbage collection run times.
The Threads tab lists the number of live threads. You can select a specific thread in the list, and see its stack trace.
The Classes tab allows you to watch how many classes are loaded in the system at any one time. If you check the Verbose Output checkbox, output is dumped to the console as each additional class is loaded. These might be system generated classes, and not necessarily classes that you recognize.
The MBeans tab is essentially a replacement for the older browser-based Agent View. Here you can find your bean and modify its attributes or execute its operations.
The final tab is labeled VM for virtual machine. It displays the details about your runtime platform as well as Operating System information.
One thing to keep in mind: your programs don't need to be or use MBeans to take advantage of the jconsole program. You can use jconsole with any program. Just be sure to start your program with the -Dcom.sun.management.jmxremote option before starting jconsole.
There's much more to JMX than what was covered in this tip. For more information about JMX, see the following documents:
Also see the Java Management Extensions page for links to additional information and resources.
|
|
 |
 |
|
|
 |
 |
IMPORTANT: Please read our Licensing, Terms of Use, and Privacy policies:
http://developer.java.sun.com/berkeley_license.html
http://www.sun.com/share/text/termsofuse.html
Privacy Statement: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your email preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe.
Comments? Send your feedback on the Core Java Technologies Tech Tips to: http://developers.sun.com/contact/feedback.jsp?category=newslet
Subscribe to other Java developer Tech Tips:
- Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).
- Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).
To subscribe to these and other Sun Developer Network publications:
- Go to the Sun Developer Network Subscriptions page, choose the newsletters you want to subscribe to and click
"Submit".
- To unsubscribe, go to the subscriptions page, uncheck the appropriate checkbox, and click "Submit".
ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at:
http://java.sun.com/developer/JDCTechTips/index.html
Copyright 2005 Sun Microsystems, Inc. All rights reserved.
4150 Network Circle, Santa Clara, CA 95054 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/developer/copyright.html
Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks (http://www.sun.com/suntrademarks/) of Sun Microsystems, Inc. in the United States and other countries.
|