Agile FAQs
  About   Slides   Home  

 
Managed Chaos
Naresh Jain’s Random Thoughts on Software Development and Adventure Sports
     
`
 
Discovering...
Industrial Logic

Microblog Feed
    Previous Feeds...
    Recent Thoughts

    Recent Comments
    Categories
    Archives
    March 2010
    M T W T F S S
    « Feb    
    1234567
    891011121314
    15161718192021
    22232425262728
    293031  
    RSS Feed
    Add to Technorati Favorites

    Archive for the ‘Java’ Category

    Setting up Tomcat Cluster for Session Replication

    Monday, November 9th, 2009

    If you have your web application running on one tomcat instance and want to add another tomcat instance (ideally on a different machine), following steps will guide you.

    Step 1: Independently deploy your web application (WAR file) on each instance and make sure they can work independently.

    Step 2: Stop tomcat

    Step 3: Update the <Cluster> element under the <Engine> element in the Server.xml file (under the conf dir in tomcat installation dir) on both your servers with:

    <Engine name="<meaningful_unique_name>" defaultHost="localhost">      
         <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                  channelSendOptions="8">
              <Manager className="org.apache.catalina.ha.session.DeltaManager"
                       expireSessionsOnShutdown="false"
                       notifyListenersOnReplication="true"/>
              <Channel className="org.apache.catalina.tribes.group.GroupChannel">
                   <Membership className="org.apache.catalina.tribes.membership.McastService"
                               address="228.0.0.4"
                               port="45564"
                               frequency="500"
                               dropTime="3000"/>
                   <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                             address="auto"
                             port="4000"
                             autoBind="100"
                             selectorTimeout="5000"
                             maxThreads="6"/>
                   <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
                       <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
                   </Sender>
                   <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
                   <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
              </Channel>
              <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                     filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.css;.*\.txt;"/>
              <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
         </Cluster>
         ...
    </Engine>

    For more details on these parameters, check https://sec1.woopra.com/docs/cluster-howto.html

    Step 4: Start tomcat and make sure it starts up correctly. You should be able to access http://locahost:8080. Most default tomcat installations come with an examples web app. Try access http://localhost:8080/examples/jsp/ You should see a list of JSP files.

    Step 4.a: Also if you see catalina.out log file, you should see:

    INFO: Initializing Coyote HTTP/1.1 on http-8080
    Nov 9, 2009 9:29:43 AM org.apache.catalina.startup.Catalina load
    INFO: Initialization processed in 762 ms
    Nov 9, 2009 9:29:43 AM org.apache.catalina.core.StandardService start
    INFO: Starting service <server_name>
    Nov 9, 2009 9:29:43 AM org.apache.catalina.core.StandardEngine start
    INFO: Starting Servlet Engine: Apache Tomcat/6.0.16
    Nov 9, 2009 9:29:43 AM org.apache.catalina.ha.tcp.SimpleTcpCluster start
    INFO: Cluster is about to start
    Nov 9, 2009 9:29:43 AM org.apache.catalina.tribes.transport.ReceiverBase bind
    INFO: Receiver Server Socket bound to:/<server_ip>:4000
    Nov 9, 2009 9:29:43 AM org.apache.catalina.tribes.membership.McastServiceImpl setupSocket
    INFO: Setting cluster mcast soTimeout to 500
    Nov 9, 2009 9:29:43 AM org.apache.catalina.tribes.membership.McastServiceImpl waitForMembers
    INFO: Sleeping for 1000 milliseconds to establish cluster membership, start level:4

    Step 5: Stop tomcat.

    Step 6: We’ll use the examples web app to test if our session replication is working as expected.

    Step 6.a: Open the Web.xml file of the “examples” web app in your webapps. Mark this web app distributable, by adding a <distributable/> element at the end of the Web.xml file (just before the </web-app> element)

    Step 6.b: Add the session JSP file. This JSP prints the contents of the session and also adds/increments a counter stored in the session.

    Step 6.c: Start tomcat on both machines

    Step 6.d: You should see the following log in catalina.out

    Nov 9, 2009 9:29:44 AM org.apache.catalina.ha.tcp.SimpleTcpCluster memberAdded
    INFO: Replication member added:org.apache.catalina.tribes.membership.MemberImpl[tcp://{-64, -88, 0, 101}:4000,{-64, -88, 0, 101},4000, alive=10035,id={68 106 92 39 -110 -8 73 124 -116 -122 -15 -3 11 117 56 105 }, payload={}, command={}, domain={}, ] 
     
    Nov 9, 2009 9:29:49 AM org.apache.catalina.ha.session.DeltaManager start
    INFO: Register manager /examples to cluster element Engine with name <server_name>
    Nov 9, 2009 9:29:49 AM org.apache.catalina.ha.session.DeltaManager start
    INFO: Starting clustering manager at /examples
    Nov 9, 2009 9:29:49 AM org.apache.catalina.ha.session.DeltaManager getAllClusterSessions
    WARNING: Manager [localhost#/examples], requesting session state from org.apache.catalina.tribes.membership.MemberImpl[tcp://{-64, -88, 0, 101}:4000,{-64, -88, 0, 101},4000, alive=15538,id={68 106 92 39 -110 -8 73 124 -116 -122 -15 -3 11 117 56 105 }, payload={}, command={}, domain={}, ]. This operation will timeout if no session state has been received within 60 seconds.
    Nov 9, 2009 9:29:49 AM org.apache.catalina.ha.session.DeltaManager waitForSendAllSessions
    INFO: Manager [localhost#/examples]; session state send at 11/9/09 9:29 AM received in 101 ms.
     
    Nov 9, 2009 9:29:49 AM org.apache.catalina.core.ApplicationContext log
    INFO: ContextListener: contextInitialized()
    Nov 9, 2009 9:29:49 AM org.apache.catalina.core.ApplicationContext log
    INFO: SessionListener: contextInitialized()
    Nov 9, 2009 9:29:50 AM org.apache.coyote.http11.Http11Protocol start
    INFO: Starting Coyote HTTP/1.1 on http-8080
    Nov 9, 2009 9:29:50 AM org.apache.jk.common.ChannelSocket init
    INFO: JK: ajp13 listening on /0.0.0.0:8009
    Nov 9, 2009 9:29:50 AM org.apache.jk.server.JkMain start
    INFO: Jk running ID=0 time=0/49  config=null
    Nov 9, 2009 9:29:50 AM org.apache.catalina.startup.Catalina start
    INFO: Server startup in 6331 ms

    Step 6.e: Try to access http://localhost:8080/examples/jsp/session.jsp Try refreshing the page a few times, you should see the counter getting updated.

    Step 6.f: You should see the same behavior when you try to access the other tomcat server. Open another tab in your browser and hit http://<other_server_ip>:8080/examples/jsp/session.jsp

    Step 6.g: At this point we know the app works fine and the session is working correctly. Now we want to check if the tomcat cluster is replicating the session info. To check this, we want to pass the session from server 1 to session 2 and see if it increments the counter from where we left.

    Step 6.h: Before accessing the page, make sure you copy the j_session_id from server 1 (displayed on the http://localhost:8080/examples/jsp/session.jsp). Also make sure to clear all cookies from server 2. (All browsers give you a facility to clear cookies from a specific host/ip).

    Step 6.i: Now hit http://<server_2_ip>:8080/examples/jsp/session.jsp;jsessionid=<jsession_id_from_server1>

    Step 6.j: If you see the counter incrementing from where ever you had left, congrats! You have session replication working.

    Step 6.k: Also catalina.out log file should have:

    Nov 9, 2009 9:42:03 AM org.apache.catalina.core.ApplicationContext log
    INFO: SessionListener: sessionCreated('CDC57B8C5CFDFDDC2C8572E7D14C0D28')
    Nov 9, 2009 9:42:03 AM org.apache.catalina.core.ApplicationContext log
    INFO: SessionListener: attributeAdded('CDC57B8C5CFDFDDC2C8572E7D14C0D28', 'counter', '1')
    Nov 9, 2009 9:42:05 AM org.apache.catalina.core.ApplicationContext log
    INFO: SessionListener: attributeReplaced('CDC57B8C5CFDFDDC2C8572E7D14C0D28', 'counter', '2')

    While this might like smooth, I ran into lot of issues when getting to this point. Following are some trap routes I ran into:

    1) java.sql.SQLException: No suitable driver tomcat cluster
    Make sure your DB Driver jar (in our case mysql-connector-java-x.x.xx-bin.jar) is in tomcat/lib folder

    2) In catalina.org if you see the following exception:

    Nov 7, 2009 3:48:53 PM org.apache.catalina.ha.session.DeltaManager requestCompleted
    SEVERE: Unable to serialize delta request for sessionid [1F43C3926FF3CC231574EF248896DCA6]
    java.io.NotSerializableException: com.company.product.Class
    	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
    	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
    	at java.util.ArrayList.writeObject(ArrayList.java:570)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    	at java.lang.reflect.Method.invoke(Method.java:597)

    This means that you are storing com.company.product.Class object (or some other object that holds a reference to this Object) in your session. And you’ll need to make com.company.product.Class implement Serializable interface.

    3) In your catalina.out log if you see

    INFO: Register manager /<your_app_name> to cluster element Engine with name <tomcat_engine_name>
    Nov 7, 2009 11:56:20 AM org.apache.catalina.ha.session.DeltaManager start
    INFO: Starting clustering manager at /<your_app_name>
    Nov 7, 2009 11:56:20 AM org.apache.catalina.ha.session.DeltaManager getAllClusterSessions
    INFO: Manager [localhost#/<your_app_name>]: <strong>skipping state transfer. No members active in cluster group</strong>.

    If both your tomcat instance are up and running, then check if your tomcat servers can communicate with each other using Multicast with the following commands:

    $ ping -t 1 -c 2 228.0.0.4
    PING 228.0.0.4 (228.0.0.4): 56 data bytes
    64 bytes from <server_1_ip>: icmp_seq=0 ttl=64 time=0.076 ms
    64 bytes from <server_2_ip>: icmp_seq=0 ttl=64 time=0.645 ms

    — 228.0.0.4 ping statistics —
    1 packets transmitted, 1 packets received, +1 duplicates, 0.0% packet loss

    or

    $ sudo tcpdump -ni en0 host 228.0.0.4
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on en0, link-type EN10MB (Ethernet), capture size 65535 bytes
    22:11:50.016147 IP <server_1_ip>.45564 > 228.0.0.4.45564: UDP, length 69
    22:11:50.033336 IP <server_2_ip>.45564 > 228.0.0.4.45564: UDP, length 69
    22:11:50.516746 IP <server_1_ip>.45564 > 228.0.0.4.45564: UDP, length 69
    22:11:50.533613 IP <server_2_ip>.45564 > 228.0.0.4.45564: UDP, length 69

    If you don’t see the results as described above, you might want to read my blog on Enabling Multicast.

    • Share/Bookmark

    Unable to initialize TldLocationsCache

    Thursday, July 9th, 2009

    On one of the projects we are using Cargo Maven Plugin to run an embedded Jetty server for our builds. Out of the blue, today, I started getting the following error when I was running my Selenium Tests after deploying the application.

    1
    2
    3
    4
    5
    6
    7
    
    WARN:  Nested in org.apache.jasper.JasperException: org.apache.jasper.JasperException: Unable to initialize TldLocationsCache: null:
    org.apache.jasper.JasperException: Unable to initialize TldLocationsCache: null
    at org.apache.jasper.compiler.TldLocationsCache.init (TldLocationsCache.java:253)
    at org.apache.jasper.compiler.TldLocationsCache.getLocation (TldLocationsCache.java:224)
    at org.apache.jasper.JspCompilationContext.getTldLocation (JspCompilationContext.java:526)
    at org.apache.jasper.compiler.Parser.parseTaglibDirective (Parser.java:422)
    ...

    No clue why this is happening. Surprising this is, this issue cannot be reproduced on a Windows box. Only on my Mac with JDK 1.6 and Maven 2.0, I’m getting this issue.

    On goolging for this issue, I make across this bug report which kind of indicated that this might be an issue with the Cargo Maven Plugin. On upgrading the plugin to version 1.0, the issue was solved. :)

    Need to find out what caused the problem in the first place.

    • Share/Bookmark

    Brett’s Refactoring Exercise Solution Take 1

    Saturday, June 13th, 2009

    Recently Brett Schuchert from Object Mentor has started posting code snippets on his blog and inviting people to refactor it. Similar to the Daily Refactoring Teaser that I’m conducting at Directi. (I’m planning to make it public soon).

    Following is my refactored solution (take 1) to the poorly written code.

    Wrote some Acceptance Test to understand how the RpnCalculator works:

    Then I updated the perform method to

    @Deprecated
    public void perform(final String operatorName) {
      perform(operators.get(operatorName));
    }
     
    public void perform(final Operator operator) {
      operator.eval(stack);
      currentMode = Mode.inserting;
    }

    Notice I’ve deprecated the old method which takes String. I want to kill primitive obsession at its root.

    Had to temporarily add the following map (this should go away once our deprecated method is knocked off).

    private static Map operators = new HashMap() {
      {
        put("+", Operator.ADD);
        put("-", Operator.SUBTRACT);
        put("!", Operator.FACTORIAL);
      }
     
      @Override
      public Operator get(final Object key) {
        if (!super.containsKey(key)) {
          throw new MathOperatorNotFoundException();
        }
        return super.get(key);
      }
    };

    Defined various Operators

    private static abstract class Operator {
      private static final Operator ADD = new BinaryOperator() {
        @Override
        protected int eval(final int op1, final int op2) {
          return op1 + op2;
        }
      };
     
      private static final Operator SUBTRACT = new BinaryOperator() {
        @Override
        protected int eval(final int op1, final int op2) {
          return op2 - op1;
        }
      };
     
      private static final Operator FACTORIAL = new UnaryOperator() {
        @Override
        protected int eval(final int op1) {
          int result = 1;
          int currentOperandValue = op1;
          while (currentOperandValue &gt; 1) {
            result *= currentOperandValue;
            --currentOperandValue;
          }
          return result;
        }
      };
     
      public abstract void eval(final OperandStack stack);
    }

    Declared two types of Operators (BinaryOperator and UnaryOperator) to avoid duplication and to make it easy for adding new operators.

    public static abstract class BinaryOperator extends Operator {
      @Override
      public void eval(final OperandStack s) {
        s.push(eval(s.pop(), s.pop()));
      }
     
      protected abstract int eval(int op1, int op2);
    }

    and

    public static abstract class UnaryOperator extends Operator {
      @Override
      public void eval(final OperandStack s) {
        s.push(eval(s.pop()));
      }
     
      protected abstract int eval(int op1);
    }
    • Share/Bookmark

    Cobertura Gottachs

    Friday, October 3rd, 2008

    Today I spent a good 3 hours trouble shooting issues with code coverage on a legacy project. I’ve been using Cobertura for a good 3 years now. Hence I decided to use Cobertura to help a team working on a legacy project. What looked like a simple 10 mins job, ended up taking forever.

    To start off, the team did not have an automated build. So I quickly put an Ant script together. Last 2 days I had helped them write some acceptance tests using FitNesse. I set up the build file to run the FitNesse tests as part of the build. Once I had the testing in place, I wanted to see what kind of code coverage we had. So we started adding Cobertura Ant tasks to the build. This was a fairly trivial task. But surprisingly cobertura-report kept displaying N/A for Line and Branch coverage. Clearly there was something wrong.

    After spending a good amount of time, I realized that we need to compile the source code with debug=true option, else Cobertura does not generate any coverage numbers. What’s amazing is, it does not complain (in the normal mode) about this nor does cobertura’s documentation talks about this. When I ran the ant build in verbose mode, it showed the following warning

    [cobertura-instrument] WARN visitEnd, No line number information found for class

    That’s when it occurred to me that I need to compile my source code with debug=true option.

    Once I solved this problem I hit the next roadblock. Even thought I fixed the debug=true option issue, cobertura-report was still displaying N/A for Line and Branch coverage. I quickly wrote a dummy unit tests and that seemed to work. Cobertura report started showing some numbers. But I was not able to generate any coverage numbers from the main Java application (server) that I was trying to test.

    I knew there was some problem with the Shutdown hook not been executed correctly to flush the coverage numbers to the ser file. It turned out that if you have a server which is started as a daemon process in a forked JVM in the build, it needs to have a way to shutdown the server gracefully. So I ended up writing a shutdown command for the server which would basically do a System.exit(0). One need to explicitly call the shutdown script before calling the cobertura report target.

    Finally after 3 hrs, there was some ray of hope in my life when I saw a good 86% code coverage. This meant I could now go and refactor the code till I dropped dead.

    • Share/Bookmark

    toString(), equals() and hashCode(): To Override or Not to?

    Sunday, August 31st, 2008
    During a pairing session with Michael Feathers, he told me why he does not like to override toString(), equals() and hashCode() methods in Java.
    If you look around, different people use toString() method to return random pieces of text. The guidelines says that the toString() method should
    Returns a string representation of the object. In general, the 

    1
    
    toString

     method returns a string that “textually represents” this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.

    What does string representation of the object really mean? Does it mean all the values of all the instance variables? Does it mean just a selected values? Usually developers decide on some selected value which uniquely identifies the object. But there is no clear guideline. 
    Similarly when we talk about the equals() method, different developers look at object equality differently. Depending on the state of the object and the context, one might have to think about equality differently. So instead of overriding equals() method, Michael prefers to create methods like matchesId() or matchesName() which are very explicit in terms of what they are matching to. 
    So the next time you override one of these methods think about them.
    • Share/Bookmark

    @Override annotation, I’m looking at you

    Monday, August 18th, 2008

    Being out of touch with Java coding and moving between Java 5 and 6 can be a great way to assume yourself. Couple of days back I stumbled upon a fairly well know Java 5 feature, @Override annotation. Annotations were introduced in Java 5. According to the JavaDoc

    public @interface Override

    Indicates that a method declaration is intended to override a method declaration in a superclass. If a method is annotated with this annotation type but does not override a superclass method, compilers are required to generate an error message.

    One would read this and think the following code should work perfectly fine:

    1
    2
    3
    
    interface I {
      void m1();
    }
    1
    
    class C implements I {
    1
    2
    3
    4
    
      @Override
      public void m1() {
      }
    }

    But guess what? In Java 5, the compiler gives the following error: “The method m1() of type C must override a superclass method”

    The same exact code works perfectly fine on Java 6. You go back and read the Javadocs again and you can’t seem to find a fault. But it turns out that in Java 5, @Override annotation only applied to methods overridden from a superclass and not from Interfaces. (So if you implement a method from an Interface, @Override annotation cannot be applied in Java 5).
    This was apparently a bug in Java 5, which was fixed in Java 6. Here is the bug report : http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6399361
    This is great, but there is no mention on public JDK 6 JavaDocs nor in the release notes.
    Eclipse IDE 3.3.1.1 recognizes the new feature and add the @Override annotation auto-magically every time I override a method or implement an interface method. Pretty cool! Unfortunately for all you Intelij fans, there is an open bug still waiting to be fixed.
    • Share/Bookmark

    svn: Unable to open an ra_local session to URL

    Monday, August 4th, 2008

    Currently I’m working on adding Revision Control support to FitNesse. In the process, I’m creating a SVN adapter using svnkit library.

    Once I’ve added a file, if I try to commit the file using

    1
    2
    3
    4
    5
    6
    
    protected void commit(File file) throws SVNException {
    final SVNClientManager manager = SVNClientManager.newInstance();
    final SVNCommitClient commitClient = manager.getCommitClient();
    final File[] filesToCommit = new File[] { file };
    commitClient.doCommit(filesToCommit, false, "Auto Commit", false, false);
    }

    I get the following exception:

    1
    2
    3
    4
    5
    
    svn: Unable to open an ra_local session to URL
    at org.tmatesoft.svn.core.internal.wc.SVNErrorManager.error(SVNErrorManager.java:55)
    at org.tmatesoft.svn.core.internal.wc.SVNErrorManager.error(SVNErrorManager.java:40)
    at org.tmatesoft.svn.core.wc.SVNCommitClient.doCommit(SVNCommitClient.java:582)
    at org.tmatesoft.svn.core.wc.SVNCommitClient.doCommit(SVNCommitClient.java:549)

    Trying to Google for this, did not take me anywhere. Finally after debugging thru svnkit’s code, I stumbled upon the following line which throws the exception:

    1
    
    SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIDecoded(url));

    Basically, they have SVNRepositoryFactory class which expects the client to register an appropriate driver to handle the given protocol. Their javadocs says:

    Depending on what protocol a user exactly would like to use to access the repository he should first of all set up an appropriate extension of this factory. So, if the user is going to work with the repository via the custom svn-protocol (or svn+xxx) he initially calls

    1
    
    SVNRepositoryFactoryImpl.setup();

    More details: http://svnkit.com/kb/javadoc/org/tmatesoft/svn/core/io/SVNRepositoryFactory.html

    Solution: Since I’m using File System (file://) protocol, I had to add the following line in a static block of my adapter class:

    1
    
    FSRepositoryFactory.setup();
    • Share/Bookmark

    Cannot instantiate DocumentBuilderFactory in JDK 5

    Saturday, December 30th, 2006

    Few months back I moved to JDK 5. After this move, recently, I tried running some Acceptance tests using an old fitnesse.jar, which was compiled using JDK 1.4. And guess what? I get the following exception :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    javax.xml.parsers.FactoryConfigurationError:
    Provider com.sun.org.apache.xerces.internal.jaxp.
    DocumentBuilderFactoryImpl could not be instantiated: java.lang.NullPointerException
    [java] at javax.xml.parsers.DocumentBuilderFactory.newInstance
    (DocumentBuilderFactory.java:104)
    [java] at fitnesse.util.XmlUtil.&lt;clinit&gt;
    [java] at fitnesse.wiki.WikiPageProperties.loadFromXmlStream
    [java] at fitnesse.wiki.FileSystemPage.attemptToReadPropertiesFile
    [java] at fitnesse.wiki.FileSystemPage.loadAttributes
    [java] at fitnesse.wiki.FileSystemPage.makePageData
    [java] at fitnesse.wiki.CachingPage.getData
    [java] at fitnesse.responders.run.FitClientResponder.readyToSend
    [java] at fitnesse.responders.run.PuppetResponse.readyToSend
    [java] at fitnesse.FitNesseExpediter.sendResponse
    [java] at fitnesse.FitNesseExpediter.start
    [java] at fitnesse.FitNesseServer.serve
    [java] at fitnesse.FitNesseServer.serve
    [java] at fitnesse.socketservice.SocketService$ServerRunner.run
    [java] at java.lang.Thread.run(Thread.java:595)

    If I use JDK 1.4, everything works fine. But there seems to be some backward compatibility issues with JDK 1.5.

    After trying different things for a while, I came across a site that stated that “The J2SE 1.4 platform included the ‘Crimson‘ reference implementation for JAXP 1.1. The J2SE 5 platform includes a reference implementation for JAXP 1.3 based on the Apache ‘Xerces‘ library.”

    In JDK 1.4, xerces and xalan are embedded in the JDK. In JDK 1.5 too, but the packages are under com/sun (so com.sun.org.apache….).

    So you would think all this should be transparent to developers. But no.

    To verify the above statement, I opened up the DocumentBuilderFactory in JDK 1.5 and I found

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    public static DocumentBuilderFactory newInstance() {
    try {
    return (DocumentBuilderFactory) FactoryFinder.find(
    /* The default property name according to the JAXP spec */
    "javax.xml.parsers.DocumentBuilderFactory",
    /* The fallback implementation class name */
    "com.sun.org.apache.xerces.internal.jaxp.
    DocumentBuilderFactoryImpl");
    } catch (FactoryFinder.ConfigurationError e) {
    throw new FactoryConfigurationError(e.getException(),
    e.getMessage());
    }

    }
    while the JDK 1.4‘s DocumentBuilderFactory class had:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    public static DocumentBuilderFactory newInstance()
    throws FactoryConfigurationError
    {
    try {
    return (DocumentBuilderFactory) FactoryFinder.find(
    /* The default property name according to the JAXP spec */
    "javax.xml.parsers.DocumentBuilderFactory",
    /* The fallback implementation class name */
    "org.apache.crimson.jaxp.
    DocumentBuilderFactoryImpl");
    } catch (FactoryFinder.ConfigurationError e) {
    throw new FactoryConfigurationError (e.getException(),
    e.getMessage());
    }
    }

    After all this research I still don‘t know what is the exact problem. But I have a solution.

    Solution: Luckily adding the xerces.jar to the classpath solved the problem.

    Would appreciate if someone would let me know the answer.

    • Share/Bookmark

    Beta version of Lattu released

    Monday, December 11th, 2006

    I just released the first beta version of Lattu.

    Lattu is a framework for testing Eclipse RCP Applications. It can run both Unit and Functional tests. Essentially, it is an Eclipse Plugin which provides Fitnesse extension to run unit and acceptance tests. It uses JUnit for unit testing and Abbot for GUI tests.

    More details: http://lattu.sourceforge.net

    • Share/Bookmark

    Date aware FitLibrary Fixtures

    Thursday, November 23rd, 2006

    Having problems parsing Dates in your fit/fitnesse tests?

    Here is a simple way to fix date parsing problem using FitLibrary.

    Fit Page

    |set date format as|MM/dd/yyyy|

    |date|
    |11/23/2006|

    |set date format as|dd-MMM-yyyy|

    |date|
    |23-Nov-2006|

    Fixture Class

    1
    2
    3
    4
    5
    6
    
    <br />
    public void setDateFormatAs(String format) {<br />
    ValueAdapter.registerParseDelegate(java.sql.Date.class, new SimpleDateFormat(format));<br />
    ValueAdapter.registerParseDelegate(java.util.Date.class, new SimpleDateFormat(format));<br />
    }</p>
    	<p>

    How this works?
    The ValueAdapter class holds a static HashMap called PARSE_DELEGATES which contains all the ParseDelegators. The ParseDelegator‘s job is to implement a parse method which take a String and returns the Object of your Data Type. You can register the Data Type and its respective ParseDelegator using the registerParseDelegate() method.

    In this case, my ParseDelegator is SimpleDateFormat which has the following method

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    <br />
    public Date parse(String source) throws ParseException{<br />
    ParsePosition pos = new ParsePosition(0);<br />
    Date result = parse(source, pos);<br />
    if (pos.index == 0)<br />
    throw new ParseException("Unparseable date: " + source ,<br />
    pos.errorIndex);<br />
    return result;<br />
    }</p>
    	<p>

    All that matters is the following method signature:
    public Date parse(String source){

    You can use the same mechanism to parse any object of choice.

    • Share/Bookmark
        Licensed under
    Creative Commons License
    Design by vikivix