Monday Dec 10, 2012

Tip #19 Module Private Visibility in OSGi

I hate public and protected methods and classes.  It requires so much work to change them in a huge project like GlassFish.  Not to mention that you may well have to support those APIs forever.  They are highly overused in GlassFish.  In fact I'd bet that > 95% of classes are marked as public for no good reason.  It's just (bad) habit is my guess.

private and default visibility (I call it package-private) is easier to maintain.  It is much much easier to change such classes and methods around. 

If you have ANY public method or public class in GlassFish you'll need to grep through a tremendous amount of source code to find all callers.  But even that won't be theoretically reliable.  What if a caller is using reflection to access public methods?  You may never find such usages.

If you have package private methods, it's easy.  Simply grep through all the code in that one package.  As long as that package compiles ok you're all set.  There can' be any compile errors anywhere else.  It's a waste of time to even look around or build the "outside" world.

 So you may be thinking: "Aha!  I'll just make my module have one giant package with all the java files.  Then I can use the default visibility and maintenance will be much easier.  But there's a problem.  You are wasting a very nice feature of java -- organizing code into separate packages.  It also makes the code much more encapsulated.  Unfortunately to share code between the packages you have no choice but to declare public visibility.

What happens in practice is that a module ends up having tons of public classes and methods that are used exclusively inside the module.  Which finally brings me to the point of this blog:

 If Only There Was A Module-Private Visibility Available

Well, surprise!  There is such a mechanism.  If your project is running under OSGi that is.  Like GlassFish does!  With this mechanism you can easily add another level of visibility by telling OSGi exactly which public you want to be exposed outside of the module.  You get the best of both worlds:

  • Better encapsulation of your code so that maintenance is easier and productivity is increased.
  • Usage of public visibility inside the module so that you can encapsulate intra-module better with packages.

How I do this in GlassFish:

  1. Carefully plan out at least one package that will contain "true" publics.  This is the package that will be exported by OSGi.  I recommend just one package.
  2. Here is how to tell OSGi to use it in GlassFish -- edit osgi.bundle like so:
    -exportcontents:     org.glassfish.mymodule.truepublics;  version=${project.osgi.version}
  3. Now all publics declared in any other packages will be visible module-wide but not outside the module.

There is one caveat:

Accessing "module-private" items outside of the module is controlled at run-time, not compile-time.  The compiler has no clue that a public in a dependent module isn't really public.  it will happily compile it.  At runtime you will definitely see fireworks.  The good news is that you don't have to wait for the code path that tries to use the "module-private" items to fire.  OSGi will complain loudly when that module gets loaded.  OSGi will refuse to load it.  You will see an error like this:

remote failure: Error while loading FOO: Exception while adding the new configuration : Error occurred during deployment: Exception while loading the app : org.osgi.framework.BundleException: Unresolved constraint in bundle com.oracle.glassfish.miscreant.code [115]: Unable to resolve 115.0: missing requirement [115.0] osgi.wiring.package; (osgi.wiring.package=org.glassfish.mymodule.unexported). Please see server.log for more details.

That is if you accidentally change code in module B to use a public that is really a "module-private" in module A, then you will see the error immediately when you try to test whatever you were changing in module B.


Sunday May 20, 2012

Tip #17 Don't Be Lazy #2

I was debugging some GlassFish code today.  This one Admin Command needed to find out if a given node was local or remote.  It is (obviously, clearly)  impossible for the node to change this attribute during this command call.  The code calls this method:

node.isLocal()

Now if you write a final getter method this is perfectly OK because the compiler will simply inline the code so that it becomes a simple variable access.

But NO WAY is that happening in this case.  The "node" object is a proxy. The value needs to get processed through code that resolves tokens, etc.  The stack shows 8 calls to get this value.  So it is an expensive call.

 I had a breakpoint in this method.  It was called over TWENTY times by the same command!  That's 152 or so pointless method calls.

What it needed was just ONE line of code:

boolean isLocal = node.isLocal();


Saturday May 19, 2012

Tip #16 Don't Be Lazy

Hmmm.  What's wrong with the code below?

I don't know if I've ever seen code screaming out louder for re-factoring.  Obviously there should be ONE method with the 4 lines that are repeated over and over and over and over.  What happens when one of the lines changes?  Well, then FOUR lines will have to change.  Of course if these copy&pasted lines are spread around in a huge file you'll probably miss a few. 

The only reason this can possibly happen is because copy&paste exists.  If we had to type in those identical blocks again and again -- THEN we would re-factor into a method call!

       if (!env.isDas()) {
            String msg = Strings.get("notAllowed");
            logger.warning(msg);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setMessage(msg);
            return;
        }

        // Make sure Node is valid
        theNode = nodes.getNode(node);
        if (theNode == null) {
            String msg = Strings.get("noSuchNode", node);
            logger.warning(msg);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setMessage(msg);
            return;
        }

        if (lbEnabled != null && clusterName == null) {
            String msg = Strings.get("lbenabledNotForStandaloneInstance");
            logger.warning(msg);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setMessage(msg);
            return;
        }       

        if (demo != null) {
            String msg = Strings.get("demoOnly");
            logger.warning(msg);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setMessage(msg);
            return;
        }

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.

Clear?

Wednesday Jun 15, 2011

Tip #1 Finding Source Files In a Gazillion Modules

 Tip #1  June 15, 2011

 GlassFish has a LOT of modules (463 pom.xml files) and a lot of java files (7810).  Here is an easy way to instantly locate source files:

    1. From the source root (Windows) run
      dir *.java /s/b >\gffiles.txt
    2. Create a script with this in it
      grep -i %1 \gffiles.txt
    3. Run the script and you'll see where the java file lives.
    4. Update once in a while -- or just wait until you don't find a match.

Example:

d:\gf\v3>gf411 counts

d:\gf\v3>grep -i counts d:\gffiles.txt
d:\gf\v3\admin\javax.management.j2ee\src\main\java\javax\management\j2ee\statistics\CountStatistic.java
d:\gf\v3\admin\monitor\src\main\java\com\sun\enterprise\admin\monitor\stats\CountStatisticImpl.java
d:\gf\v3\admin\monitor\src\main\java\com\sun\enterprise\admin\monitor\stats\MutableCountStatistic.java
d:\gf\v3\admin\monitor\src\main\java\com\sun\enterprise\admin\monitor\stats\MutableCountStatisticImpl.java
d:\gf\v3\common\stats77\src\main\java\org\glassfish\j2ee\statistics\CountStatistic.java

Note:  I finally did this after dozens (scores?  hundreds?) of times manually searching for files.  Don't waste your time!


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()) { ..... }

Why?

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.

Friday Oct 05, 2007

How to start JavaDB/Derby as a Windows Service

As I've recently started using the built-in Java DB support in GlassFish, I want the Java DB Network Server to start automatically along with GlassFish.

 I discovered that I can use the exact same method and program that's used for GlassFish-as-a-service.

On Windows, I used this script to setup a Windows Service that automatically starts the Java DB Network Server.  You can change the paths, run it once and the DB will start automatically as a service.

 

@echo off
if "%1A"=="A" goto usage
if "%2A"=="A" goto usage

echo on
sc create %1 binPath= "C:\\as\\lib\\appservService.exe \\"C:\\as\\bin\\asadmin.bat start-database --dbhome %2\\" \\"C:\\as\\bin\\asadmin.bat stop-database\\""  start= auto DisplayName= %1
goto end

:usage
echo usage createDBservice  name dbhome

:end


 

About

ByronNevins

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today