Tuesday Aug 09, 2011

Tip #4: Stop and Think before you make that Class public

I run into this time-wasting situation fairly frequently:

I need to change the signature of a Java class, in GlassFish,  that has been declared public.  Uh-oh!  It's public so I can't just change it. 

So -- my first step is to grep through every single java file in GlassFish.  That means  7,772 files currently!

Now I have to wonder -- is the class being used from modules outside of GlassFish?  Uh-oh -- now I have to run massive automated tests to make sure I don't break something.

Ultimately you can never be totally sure that public class is not being used somewhere.  E.g. say the name of the class is FooGooHoo.  What if there is code doing a Class.forName("Foo" + "Goo" + "Hoo") ?  I'll never find it with a simple search. 

This is all expected stuff with the public keyword.  But what if the class is exclusively used inside of its own package?  Then I just  wasted a whole lot of my employer's money chasing down non-existent references. 

On the other hand if the class had its visibility set to pkg-private in the first place, all I had to do was to search inside that one package for uses.  Java guarantees no other package can see it.  And if other code somehow subverts that policy -- tough!!

Moral of the Story:

Only mark classes and methods as public when you are absolutely positively sure that it must be public.  When in doubt make it pkg-private and avoid protected too.

Monday Aug 08, 2011

Tip #3: Make Life Easier for Your Code Reviewer

There are probably a hundred applications that will package up code changes in various cool ways.  You could also write your own -- unfortunately it's one of those cases where the time you save using the tool will never approach the time you spent making the tool. 

I get code review requests fairly often and they are almost always like this:

 Below are the diffs for my change.  Please review

 And as promised the output of some diff tool is below in the email.  What if the context around the diffs isn't big enough?  What if I hate the diff tool formatting?  What if I want to compile and run the actual changes in my environment?

I can't do any of the above quickly and easily with the raw diffs embedded in the email.  Even copying and pasting them is usually a big pain because of the prepended plus signs.

 Here are my requirements for code review submitters (Please?!?)

  • Send out the actual changed source files
    • Make it very very easy to get those source file into the right directory.  Don't make me type in commands with endlessly long paths!
  • Send me the diffs as a file and have it appear directly in the email (maybe the change is easy and I don't need to waste time dealing with file attachments)

And here is the solution I use.  It is simple and old-fashioned and works great:

  1.  Create the following script (Windows version).  I use this script constantly when I'm working on source code.  That's why I named it "s.bat".  Usually I don't use the "dome.bat" file but I always create it just in case.  I sort the output to get all the modified files together.

    svn status | sort | tee dome.bat

  2. To submit the changes to a code reviewer, I want him or her to get the diffs file, and all changed files.  I collect the diffs in a file like so (make a script out of it!)
    svn diff > diffs.txt
  3. Here is the trick.  Edit the dome.bat file from step 1 above.  Delete the uninteresting lines and QUICKLY edit the Modified or Added lines to do this:
    jar cvfM mychanges.jar diffs.txt c:/some/gigantic/ugly/long/path/foo.java c:/some/even/uglier/gigantic/long/path/foo2.java
  4. Now I attach mychanges.jar to an email and also copy the diffs to the email message.  I tell the reviewer which directory to change to and how to unpack the source files.  E.g.
    "cd to core/kernel and run jar xvf mychanges.jar to overlay my changes in your installation"

It sounds like a big deal but it shouldn't take more than a couple minutes and it makes life more pleasant for the reviewer.  Note how the reviewer can do this:

jar cvfM temp.zip src
jar xvf mychanges.jar
run his or her favorite diff tools
compile and build, look at the code in a debugger or whatever
when review is complete, jar xvf temp.zip

Thursday Jul 28, 2011

Tip #2 Step Through Every Line of Code in a Debugger

This is so simple.  The place where I catch most of my bugs -- before the code is even checked-in.  


Step Through Every Line of Code You Write in a Debugger

 I'm frequently amazed at what I bump into during this pass-through.  I've found all sorts of embarrassing problems.  You never see them, though, because I caught them before checking in.

Nobody writes bug-free code.  But you can check-in bug-free (or nearly so) code.

Some people may counter with: "Oh ho!  I have no time to step through the code in a debugger!"  My answer is that the time it takes to  step through the code in a debugger is normally a tiny percentage of the time it takes to do all of these things:

  • Think up what to write
  • Write it
  • Fix compile errors etc.
  • Fix more obvious errors from simple tests
  • write unit tests
  • write dev tests
  • close an issue in Issue Tracker
  • check-in comments
  • Ask for a code review, get comments, respond. make changes

And I mean every line of code.  Modern java debuggers allow you to change values of variables, etc., to force your execution path into error handling code.

If you start do this zealously it may force you into picking up other good development habits too, that make it easier to step-through all of your code.

Tuesday Jul 19, 2011

Tip #-1 How Does GlassFish Pick the JVM to Run In?

 If you start a GlassFish Server by using asadmin, then you may be interested in precisely how the JVM that will run GlassFish Server is chosen.  Note that if you restart via the Admin Console (GUI) you are indirectly using asadmin to restart the server and the following rules apply as well.  If you start the Server some other way, like

java -jar glassfish.jar

then you are in charge of choosing the JVM yourself.

In order of precedence from high to low:

  1. The jdk location specified in domain.xml in the java-config  ("java-home")
  2. The contents of AS_JAVA in the asenv property file (in glassfish/config)
  3. Whatever the env. variable, JAVA_HOME, is pointing at.
  4. The java.home system property is used like so:
    1. Parent directory of java.home -- this is because sometimes the JRE's java is asadmin's JVM and the JDK may be in the directory above.
    2. Contents of java.home

In every case I guarantee a valid java.  E.g. you can set 1,2,3 to garbage and we'll still find a java to use.

Note that we never explicitly use java from the path -- if there is one.  It may be implicitly used though by using #4 above since asadmin itself is definitely running inside of a valid JVM.

p.s.  If you are wondering where the code is -- 90% of it is inside AsenvPropertyReader.java
10% is in GFLauncher.java

-- AsenvPropertyReader can't access domain.xml.  GFLauncher can.  GFLauncher's job is to see if (1) above applies.  If not he defers to AsenvPropertyReader.


Thursday Jun 12, 2008

Tip #-2 Never lock on a Class

I just learned a lesson today the usual way.  I.e. the hard way!  

Conclusion -- never do this:

synchronized(getClass()) { ..... }


The intent is obviously to serialize the block against ALL instances.  About the only requirement is that the object be unique and global.

Oops!  The object is not unique!  What if the method is called from a derived class?  Then the lock object is totally different.  That is what happened to me.

This is a much better way to do such things:

class Foo {

private final static Object myLock = new Object();

public void someMethod() {

    synchronized(myLock) { ....... }

myLock is global, unique and is self-documenting.

Tuesday Apr 24, 2007

Tip #-3 How to Debug Clustered AppServer Instances

There used to be a big problem in trying to attach a debugger to a clustered server instance in Glassfish.  As of today (build 45), it is a snap to do it!

The problem was that a cluster config could only specify one debugging port.  This would work if you started one and only one server instance.  As soon as you tried to start a second instance it would try to use the same jdwp port and it would fail to launch.  It was (almost) impossible to debug 2 instances on the same machine at the same time.

The solution is to defer the actual port number selection.  I.e. the instance needs to choose its own port.  Each instance specifies a different port and, voila, problem solved.

Here is how to do it:

1. Enable debugging support for the cluster
We are using a cluster named c1.  First make sure that debugging is enabled for the cluster:

asadmin set c1-config.java-config.debug-enabled=true

2. Set a variable debugging port

The syntax is ugly.  Don't bother memorizing just do a get to see it, edit it and then do a set on the result like so: 

asadmin get c1-config.java-config.debug-options
c1-config.java-config.debug-options = -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9009
asadmin set c1-config.java-config.debug-options="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${DEBUG_PORT}"
Don't forget the double quotes, and no spaces around the equals sign!

 Another Tip:  asadmin get "\*" > afile is handy.  You can then grep for 'debug' in the file.  I use this technique all the time.  What human being wants to memorize this sort of crushing detail?

3. Set the instances' debugging ports

 In this case we have 2 instances, i1 and i2.  We will make the debugging ports 11111 and 22222 respectively:

GlassFish v2.x:

asadmin set i1.system-property.DEBUG_PORT=11111
i1.system-property.DEBUG_PORT = 11111
asadmin set i2.system-property.DEBUG_PORT=22222
i2.system-property.DEBUG_PORT = 22222

The above does NOT work in GlassFish 3.X.  Instead use the following commands:

asadmin create-system-properties --target i1 DEBUG_PORT=11111
asadmin create-system-properties --target i2 DEBUG_PORT=22222

That's it.  You're ready to debug multiple instances.




« July 2016