 |
Compiling the JMX API in Mustang
Posted by emcmanus on August 30, 2005 at 9:10 AM PDT
The Java SE sources are downloadable from java.net, and you can
change and extend them within the constraints of the relevant
licenses. But building the whole of Java SE is a major
undertaking. Here's how to build just the JMX API. The same
ideas would apply to building other subsets of Java SE that are
Java-language-only.
Update: there is now a purpose-designed build.xml that ships
with the Mustang sources and that provides a simpler alternative to what's
described here for the JMX API. See
Joël Féraud's blog. But the process described here is
still useful for building other subsets of the Mustang API.
Why is this interesting?
There are a number of reasons why you might be interested in
modifying and recompiling the Java SE sources:
For research purposes, as covered by the Java Research License
(JRL). This might be for your own curiosity ("I bet I
could speed up the MBeanServer implementation"). Or it
could be a good idea for a student project ("Add support for
persistence to the JMX API"; "Extend the JMX API to provide
support for reliable eventing"). Student projects that
modify an existing medium-sized code base like the JMX API
are much more instructive than projects that create new code
from scratch, as they teach the invaluable skills of finding
your way around a body of code. (I have lots more ideas for
student projects, by the way!)
As an extension to the previous idea, you may be
interested in
contributing to the JDK code base. Obviously if you
want to do this, you'll need to be able to build your
modifications or extensions to existing code.
Finally, you might have run into a critical problem in
the JMX API that you need to be able to fix right away. In
general we make every effort to fix blocking problems
promptly when they are reported, but because of the standard
release cycle, it could be a few months before you see an
update with your problem fixed. If you need the fix
right now then you may be able to make it yourself
under the terms of the
Java Internal Use License (JIUL).
The JIUL applies to the current released JDK version, JDK 5.0
("Tiger"), so the third reason above only applies there. The
JRL applies to both Tiger and the forthcoming version, JDK 6
(Mustang), so for research you can choose which one is more
appropriate. You can potentially contribute bugfixes and
performance optimizations to Tiger or Mustang, though it's
likely to be easier to contribute to Mustang. You can only
contribute API changes to Mustang, since the Tiger API is
frozen.
I should note that I am not a lawyer, and I'm not a
spokesperson for Sun either, so read the licenses and their FAQs
to be sure they cover what you want to do.
How do I build a subset of the JDK sources?
The instructions below are for building the JMX API in Mustang.
Building a subset of Tiger is actually somewhat simpler, because
you do not need to worry about "cross-platform support".
Apart from the details about RMI stubs at the end, the same
considerations would apply to building other Java-language
subsets of the JDK sources. If you need to change native code,
then things become more complicated. Fortunately the JMX API
doesn't contain any native code.
I tried out everything here on a machine running Microsoft
Windows 2000. My usual development environment is Solaris, so
trying this out on Microsoft Windows should have helped me avoid
missing any "obvious" details that might not be so obvious to
people who haven't worked on the JDK sources before! If you're
running on a Unix-like environment such as Solaris or Linux it
should be fairly obvious what to change.
To build Mustang, you'll want Mustang's JDK installation.
This is all the more true if you are only building part of
Mustang, because the JDK installation will supply the part
you're not building. Download and install the latest
Mustang JDK binaries from
http://download.java.net/jdk6/binaries/.
Check the click-through license -- this is pre-release
software and that limits what you can do with it, in obvious
ways.
You probably don't want to install this JDK snapshot in the
default location. On Microsoft Windows, it is
C:\Program Files\Java\jdk1.6.0\, which I think
confers an unwarranted legitimacy on the installation. I
put it in D:\jdk1.6.0\ -- you get to choose this
in the dialogue entitled "Custom Setup". You don't need to
change anything else in this dialogue.
The installer covers both the Java SE Development Kit (JDK)
and the Java SE Runtime Environment (JRE). In the "Custom
Setup" dialogue for the JRE, you'll likewise probably want
to change the default install location.
D:\jre1.6.0\ was the location I chose.
Download the latest Mustang source from
http://download.java.net/jdk6/. You'll
need "JDK 6.0 Source under the JRL License", and
you should take a moment to check out the license.
The "JDK Binaries for Source Build 6.0" represents binary
files such as Swing icons. You don't need these if you are
building the JMX API, but if you're following these
instructions to build some other subsystem you might.
The source snapshot you downloaded will be called something
like jdk-6_0-ea-src-b49-jrl-25_aug_2005.jar.
It is an executable jar file. On Unix-like systems you
might need to make it executable (chmod +x
filename). Then execute it to install the complete
Mustang sources.
You get another chance to look at the magnificent JRL
License before you click to accept it. Then you specify where
you want to install the sources. I put this snapshot (b49) in
D:\mustang-src-b49 to avoid confusion with any
other Mustang snapshots I might download, but you might prefer
to reuse the same directory even if you download several
snapshots.
There's a huge amount of source code in this snapshot.
To find your way around a bit, check out the
README.
All we're interested in here is the parts that build the JMX
API.
OS-independent Java platform classes can be found under
j2se\src\share\classes. Frequently, subsystems
have some classes under java.* or
javax.*, including all of the publicly-visible
API classes of course; and some implementation classes under
sun.* or com.sun.*. You can find
out pretty readily where this second set of classes lives by
examining the import statements in the first set.
It turns out that the complete set of sources needed to
build the JMX API consists of:
- All classes under
javax.management,
including all subpackages, such as
javax.management.timer and
javax.management.remote.
- All classes under
com.sun.jmx, including
all subpackages except
com.sun.jmx.snmp.
com.sun.jmx.snmp represents the source for SNMP
access to the JVM instrumentation defined by
JSR 174; this
is not directly relevant to the JMX API. You can build it
if you want, but you don't need to.
I'd suggest copying the subset you want to build into a
different directory rather than trying to build the files within
the complete hierarchy. What I did was to create another
directory D:\mustang-jmx-src-b49 with a
subdirectory src into which I copied the
javax\management and com\sun\jmx
hierarchies. (Then I removed com\sun\jmx\snmp.)
Of course you need to preserve the directory hierarchy, e.g. you
should have src\javax\management and
src\com\sun\jmx within your subset directory.
You now have a workspace within which you can build the
JMX API. Here's how I went about it using NetBeans 4.1. I
would expect the steps would be similar with other IDEs,
although if your IDE does not support cross-platform use
things will be a bit tricky. Cross-platform means that for
example I am running NetBeans using JDK version 5.0 but I am
telling it to compile and run my sources using JDK version
6. If your IDE doesn't support cross-platform use then you
might still be able to run it under the JDK 6 snapshot you
have downloaded. This is not necessarily as scary as it
sounds -- at work I usually run NetBeans with the latest
Mustang snapshot and there's only been one period of two
weeks where it didn't work well. But, nothing is guaranteed
with these snapshots, and the fact that the class file
format has changed in Mustang may prevent your IDE from
being able to compile against the JDK classes.
I first created a NetBeans project using File -> New
Project -> Java Project With Existing Sources. I specified
project name mustang-jmx-src-b49 and project
directory D:\mustang-jmx-src-b49, i.e. the
directory where I had copied the source subset. I added the
src subdirectory in the dialogue that says "Specify
the folders containing the source packages and JUnit test
packages." I specified a new test subdirectory for
JUnit tests. Once I clicked on "Finish" the project was created.
Then I set up cross-platform building and running.
NetBeans is running on JDK version 5.0 but I want it to use the JDK 6
snapshot to compile and run the sources of this project. So I
right-clicked on my mustang-jmx-src-b49 project in
the Projects pane, and selected Project Properties -> Libraries.
I needed to tell
NetBeans about the JDK 6 platform, which I did using the "Manage
Platforms" button on the top right of this dialogue. (You can
also use Tools -> Java Platform Manager.) In the Java
Platform Manager dialogue, I clicked "Add Platform..." in the
bottom right. I selected D:\jdk1.6.0, which you'll
remember is where I installed the downloaded Mustang JDK
snapshot. If you downloaded the snapshot Javadoc you could
specify it here too, but you don't have to. (I usually browse
it directly from
java.net.) Then Finish (for Add Platform) and Close
(for Java Platform Manager), and you're back to Project
Properties. You can now select the JDK 6 platform in the
Java Platform drop-down list.
Also within the Project Properties dialogue, I needed to
tell NetBeans to use compiled classes from within the
project in preference to the classes in the JDK
installation. This is subtle but important. Even if you
have a class called javax.management.ObjectName
in your classpath, the Java compiler and runtime will never
see it because they will find the class of the same name in
the JDK installation first. You need to change
the boot classpath using
the
-Xbootclasspath option for both compiling and
running.
In Project Properties -> Build -> Compiling, I put
the following in the "Additional Compiler Options":
-Xbootclasspath/p:build/classes
build/classes is the subdirectory within the
workspace where NetBeans is going to put its compiled classes.
(If you want to be squeaky clean you could put
${build.classes.dir} here instead.)
The /p means "prepend", i.e. this directory will
be put in the boot classpath before the standard classes,
which is what you want. I put the same thing in the "VM
Options" within Project Properties -> Run.
Unfortunately it appears that this
-Xbootclasspath option doesn't get applied to
the Java VM that is running the JUnit command to run tests.
I had to edit some ant files by hand. I copied from
nbproject\build-impl.xml the complete set of
lines from <target
name="-init-macrodef-junit"> through to the
corresponding </target>, and pasted them
into the top-level build.xml file near the end. Then I
added this line in the pasted text, just after the
<junit ...> line:
<jvmarg value="-Xbootclasspath/p:build/classes" />
Now to test that NetBeans really is compiling my sources
and not falling back on the JDK classes, I added a trivial
main method to
javax.management.ObjectName:
public static void main(String[] args) {
System.out.println("hello, world");
}
NetBeans now flags ObjectName.java with a
little green arrow in the Projects or Files panel because it
is runnable. If I right-click on ObjectName.java
in the panel, I can do "Run file". If this works, I know
NetBeans is compiling and running my modified
ObjectName class.
Similarly, I can code a trivial JUnit test using File ->
New File -> JUnit -> Empty Test. I just insert a test
method that calls my new ObjectName.main
method:
public void testObjectNameMain() {
ObjectName.main(new String[0]);
}
Now "Run file" on the test should successfully execute my
test and print "hello, world".
If you've followed all these steps, you're now set up to
play with the JMX API sources!
One thing to watch out for, though, is that if you delete
or rename a class, the compiler will still be able to find
it in the original JDK classes. So if there are still
references to the deleted class, they will continue to
compile! If this proves to be a serious problem, you can
get around it by preparing a "JMX-less" JDK installation.
Copy your original JDK installation to another directory,
and remove the javax.management and
com.sun.jmx packages and subpackages from
jre\lib\rt.jar within that other installation.
The jar command doesn't allow you to remove
things from a jar, but because a jar is a special kind of
zip file, you can use any tool for manipulating zip files
instead. Then use the Java Platform Manager within NetBeans
to replace the original JDK 6 platform with your JMX-less
JDK platform. On the whole, this is a dodgy manoeuvre, and
it's not guaranteed to work in the future. However, it's
what we do within the JMX API team at Sun, and if it stops
working at some point, I'll update this page. Also, I'd
expect it probably wouldn't work for classes that the
compiler itself needs to run. The compiler doesn't need the
JMX API, but you might have a hard time doing the same thing
with java.util!
A final thing to watch out for is that the JMX Remote API
includes some RMI stubs which are built using the
rmic tool. Since the source for these
generated stubs is not included in the JDK source
download, you will contine to pick them up from the JDK's
own classes. If you make a JMX-less JDK installation, or if you want to
change the RMI interfaces the stubs come from (
RMIServer and
RMIConnection), then for the JMX Remote
API to work you'll need to generate the stubs yourself.
You could add some
<rmic> tasks to your
build.xml, but it might be simpler just to
generate the source code for the stubs and include the
source in your project. For example, to make the
RMIServer stub source you could use:
rmic -keep -d src -bootclasspath build\classes;D:\jdk1.6.0\jre\lib\rt.jar
javax.management.remote.rmi.RMIServerImpl
(all on one line of course), then remove the file
src\javax\management\remote\rmi\RMIServerImpl_Stub.class
that rmic will have generated. The procedure is
the same for the RMI/IIOP stubs except you need the
-iiop option and the stubs will be generated in
org\omg\stub\javax\management\remote\rmi.
Unfortunately rmic doesn't recognize
-Xbootclasspath/p: so you have to give the full
path to the classes in your JDK installation as shown
here.
I hope this has been helpful! If you had a problem following
these instructions, or would like to suggest improvements, or if
you want to discuss API changes you are interested in, feel free
to contact me at
jmx-spec-comments@sun.com.
Update: if you want to build the whole of the JDK on a
Microsoft Windows platform, Kelly O'Hair has documented out how to do it. The hard part is installing the necessary software.
|