classpath

Classpath is a parameter—set either on the command-line, or through an environment variable—that tells the Java Virtual Machine or the Java compiler where to look for user-defined classes and packages. [read more at http://en.wikipedia.org/wiki/Classpath_(Java)]

  • Â Read this document about classloader (PDF)
    • System classloader is the content of the system variable CLASSPATH= you defined higher level
    • Loader of the servlet runner is own classloader of Resin, tomcat or other
    • Application classloader (controlled by resin or tomcat) find classes which are in the webapps directory (Application class).
    • Your own classloader if you write own lower level

    As soon as you request a class in one of these loader, the classloader will take the first found during its walk under this rules:

    • if the class is not found at the current level (place where you request it, most of the time in your application), it will try to load it from the upper level, if not found it will continue and finish in the system classloader.
    • There is no downward request.
  • Â java.lang.ClassNotFound and ClassNotDefFoundError are different but what if my classes are in classpath?

    Â Remember:

    • ClassNotDefFoundError is thrown if a class definition of the hierarchy is not located by the classloader
    • ClassNotFound if the file or class can not be found in classpath. Remember only some servletrunner can open jar files, so having jar files in a directory without having them explicitely in classpath do not help much. You must have the name of jar file in CPath.
  • apache_maven

    What can you do to avoid that when you use one Maven dependency, to also inherit some other undesirable older
    dependency (which is to say from an older transitive dependency).

    The fix to this is to add an exclusion to the dependency in question.
    For example, if we start with a dependency upon version 1.2 of the jxpath library:

    <dependency>
       <groupId>common-jxpath</groupId>
       <artifactId>common-jxpath</artifactId>
       <version>1.2</version>
       <scope>compile</scope> <!-- default scope for sake of example-->
    </dependency>

    This dependency to jxpath 1.2 will bring in an old version of log4j 3.8. In order to ensure that I am using the latest
    versions of log4j (4.4),

    I need to put in an exclusion for these transitive dependencies of common-jxpath, which I do as follows:

    <dependency>
       <groupId>common-jxpath</groupId>
       <artifactId>common-jxpath</artifactId>
       <version>1.2</version>
       <scope>compile</scope> 
       <exclusions>
          <exclusion>
             <artifactId>junit</artifactId>
             <groupId>junit</groupId>
          </exclusion>
          <!-- I can put many of these here -->
    </exclusions> </dependency>

    Having excluded them, they will be any longer in the build.

    Now, there is still too many thing that can occur in the background

    • Another 3rd party artifact may include log4j by using a transitive dependencies, and then you will have to rely/trust transitive
      dependency mediation
    • You can explicitly include the versions that you want in all pom.xml or better in your parent pom.xml

    Transitive dependency mediation

    Dependency mediation - this determines what version of a dependency will be used when multiple versions of an artifact are
    encountered. Currently, Maven 2.0 only supports using the "nearest definition" which means that it will use the version of
    the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it
    explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, until
    Maven 2.0.4 it was not defined which one would win, but since Maven 2.0.5 it's the order in the declaration that counts: the
    first declaration wins.
    "nearest definition" means that the version used will be the closest one to your project in the tree of dependencies, eg. if
    dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A
    because the path from A to D through E is shorter. You could explicitly add a dependency to D 2.0 in A to force the use of D 2.0

    find out what the transitive dependencies are?

    You can't control what you do not know!

    One that can be use during build stage or explicitly use on command line, is the maven plugin maven-dependency-plugin

       <build>
          <plugins>
             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
             </plugin>
          </plugins>
       </build>

    and then use the goal dependency:tree, so a typical build strategy could look like 

    mvn clean install dependency:tree
    or
    mvn clean install dependency:list   (easier to tokenize in excel sheet)
    So it look like
    With no exclusions

    [INFO] [dependency:tree]
    [INFO] com.test:test:jar:0.0.1-SNAPSHOT
    [INFO] \- commons-jxpath:commons-jxpath:jar:1.2:compile
    [INFO]    +- xerces:xerces:jar:1.2.3:compile
    [INFO]    +- javax.servlet:servlet-api:jar:2.2:compile
    [INFO]    +- junit:junit:jar:3.8:compile
    [INFO]    +- ant:ant-optional:jar:1.5.1:compile
    [INFO]    +- xml-apis:xml-apis:jar:1.0.b2:compile
    [INFO]    +- jdom:jdom:jar:b9:compile
    [INFO]    +- commons-beanutils:commons-beanutils:jar:1.4:compile
    [INFO]    +- commons-logging:commons-logging:jar:1.0:compile
    [INFO]    \- commons-collections:commons-collections:jar:2.0:compile
    [INFO] [dependency:list]
    [INFO]
    [INFO] The following files have been resolved:
    [INFO]    ant:ant-optional:jar:1.5.1:compile
    [INFO]    commons-beanutils:commons-beanutils:jar:1.4:compile
    [INFO]    commons-collections:commons-collections:jar:2.0:compile
    [INFO]    commons-jxpath:commons-jxpath:jar:1.2:compile
    [INFO]    commons-logging:commons-logging:jar:1.0:compile
    [INFO]    javax.servlet:servlet-api:jar:2.2:compile
    [INFO]    jdom:jdom:jar:b9:compile
    [INFO]    junit:junit:jar:3.8:compile
    [INFO]    xerces:xerces:jar:1.2.3:compile
    [INFO]    xml-apis:xml-apis:jar:1.0.b2:compile

     

    With exclusions

    [dependency:tree]
    [INFO] com.test:test:jar:0.0.1-SNAPSHOT
    [INFO] \- commons-jxpath:commons-jxpath:jar:1.2:compile
    [INFO]    +- xerces:xerces:jar:1.2.3:compile
    [INFO]    +- javax.servlet:servlet-api:jar:2.2:compile
    [INFO]    +- ant:ant-optional:jar:1.5.1:compile
    [INFO]    +- xml-apis:xml-apis:jar:1.0.b2:compile
    [INFO]    +- jdom:jdom:jar:b9:compile
    [INFO]    +- commons-beanutils:commons-beanutils:jar:1.4:compile
    [INFO]    +- commons-logging:commons-logging:jar:1.0:compile
    [INFO]    \- commons-collections:commons-collections:jar:2.0:compile
    [INFO] [dependency:list]
    [INFO]
    [INFO] The following files have been resolved:
    [INFO]    ant:ant-optional:jar:1.5.1:compile
    [INFO]    commons-beanutils:commons-beanutils:jar:1.4:compile
    [INFO]    commons-collections:commons-collections:jar:2.0:compile
    [INFO]    commons-jxpath:commons-jxpath:jar:1.2:compile
    [INFO]    commons-logging:commons-logging:jar:1.0:compile
    [INFO]    javax.servlet:servlet-api:jar:2.2:compile
    [INFO]    jdom:jdom:jar:b9:compile
    [INFO]    xerces:xerces:jar:1.2.3:compile
    [INFO]    xml-apis:xml-apis:jar:1.0.b2:compile

     
    see Maven Dependency Plugin
  • apache_maven

    In a multi modules project where you have write API or common code for unit tests in one project and want to reuse these in the tests for another project. Maven will crash during the compile phase if you do not make the following.

    Maven rules of the game:

    • The main code in src/main/java is visible across modules if you did specify project
      dependencies in pom.xml.
    • Test code reside in src/test/java and is not shared across modules, moreover
    • Test code can use any code from src/main/java but not the other way around, which
      make sense as we want to clearly separate test code (junit testcases) from code shipped.

    The solution is to create additional test jar for each module, this is done by putting in the
    parent pom (lets call it parent-pom.xml)

    inside the <build></build> tags the following:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>test-jar</goal>
            </goals>
          </execution>
        </executions>
    </plugin>

This will create for each modules an additional jar named {artifactId}-{version}-tests.jar
during the goal test-jar

Now for every modules where you want to reuse test classes, all you have to do in to put in every
modules pom.xml a dependency to that test jar by adding the tests classifier

 <dependency>
     <groupId>yourGroup</groupId>
     <artifactId>yourReusableModuleArtifact</artifactId>
     <version>0.1-SNAPSHOT</version>
     <classifier>tests</classifier>
     <scope>test</scope>
 </dependency>

 

This has work for me with Maven 2.0.8

  • Java problem N°1

    On windows NT machine, command line length is limited to 1024 characters!, clearly not enough for a distributed classpath with a lot of frameworks or 3rd party tools.

    Solutions

    • Reduce number of jar files, instead of having 10 jar files, you can try to build only one, use automatic build process, with jakarta ANT during the build and for your deployment. This is often not a good/verybad solution.
    • Put the half classpath size in java ext classpath (if you use resin or tomcat)
    example
    • Use cygwin to start the java process, for example you can use a bash terminal. But do not use cygrunsrv as it was not designed for installing java process as NT service: it wil create a ghost program when you kill the service.
    • Last chance, put the classpath in a .txt file and create a classloader which will read it and set the classpath (adaptative classloader). You must create your own classloader!
  • Parser incompatibility or Parser order in Classpath,classloader, loading sequence in a highly complex and distributed environment.

    Especially in a distributed environment, order of parser found is a major problem during the walk of the JVM in classpath. For example, Different versions of the parser SAX are co-existing in the classpath due to components restrictions :
    • Parser sax 1.0 is required for BEA Weblogic 6.0 and Xerces 1.3.1
    • Parser sax 2.0 is required for Xerces 1.4 and crimson
    • Crimson is required for APACHE SOAP 2.2
    • You may want to use Apache soap client because it still accept to run with a JVM 1.2.2

    Try to draw a graph or parser dependancy: determine for all components in your application which DOM level is required.

    Solution 1: Only use one parser Apache Xerces (XML) and only one transformer Saxon (XST) or Apache Xalan (XST). It is not always possible since some 3rd party tools, like weblogic.jar or oracle.jar are coming with some crap inside...

    For example weblogic 6.0 contains a SAX 1.0 (http://edocs.bea.com/wls/docs61/notes/migrate60to61.html) But weblogic 6.1 a SAX 2.0!!!!!

    • Extract from BEA website:
      "Apache XML Parser
      The XML WebLogic Server 6.0 -> 6.1 parser has been updated and is now based on the Apache Xerces 1.3.1 parser. The parser implements version 2 of the SAX and DOM interfaces. Users who used older parsers that were shipped in previous versions may receive deprecation messages."
    • Solutions :
      - Displace the jar file in the classpath, so it will be load after the sax2 compliant parser....
    • And ?
      - Just for being confused, with no change at all, your code may be run if you use BEA Weblogic 6.1!!!!
    • What to retain ?
      - Order of packages loading is very important when working in JAVA.
      - If you are lost, and want to know when a package or class is loaded, you can start java with this parameter: -verbose the default output is System.out
      - You need to check carefully all preconditions before using a new 3rd party component, in order to see if you will not be incompatible with others 3rd party tools.

    Solution 2: Even if you have multiple parser in classpath, you can still force javax.xml factory to use the parser/transformer you want...

    Force Factory: create 3 files and put them in front of classpath in a directory META-INF/services/

    • File named: javax.xml.parsers.DocumentBuilderFactory may contains org.apache.xerces.jaxp.DocumentBuilderFactoryImpl(or org.apache.crimson.jaxp.DocumentBuilderFactoryImpl)
    • File named: javax.xml.parsers.SAXParserFactory may contains org.apache.xerces.jaxp.SAXParserFactoryImpl(or com.icl.saxon.aelfred.SAXParserFactoryImpl)
    • File named: javax.xml.transform.TransformerFactory may contains com.icl.saxon.TransformerFactoryImpl

    OR create 3 java variables when your start your java process:

    java -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl -Djavax.xml.transform.TransformerFactory=com.icl.saxon.TransformerFactoryImpl

    OR use system variables, in your code you can type, for each variable (not very flexible):

    System.setProperty("avax.xml.transform.TransformerFactory","com.icl.saxon.TransformerFactoryImpl")