This blog is mainly about Java...

Friday, February 24, 2017

java.sql.SQLException: Protocol Violation [14, 62]

Apparently there is a bug in OJDBC 7 Oracle Driver version 12.1.0.1.0.
Take a look at https://confluence.atlassian.com/confkb/confluence-and-oracle-fail-with-protocol-violation-and-could-not-get-clob-value-for-col-errors-780863295.html

The stacktrace in question is this:
Caused by: java.sql.SQLException: Protocol Violation [ 14, 62, ]
 at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:669) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:249) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.jdbc.driver.T4C8TTIClob.read(T4C8TTIClob.java:245) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.jdbc.driver.T4CConnection.getChars(T4CConnection.java:3901) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.sql.CLOB.getChars(CLOB.java:517) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.sql.CLOB.getSubString(CLOB.java:354) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.jdbc.driver.ClobAccessor.getString(ClobAccessor.java:454) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.jdbc.driver.GeneratedStatement.getString(GeneratedStatement.java:327) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at oracle.jdbc.driver.GeneratedScrollableResultSet.getString(GeneratedScrollableResultSet.java:882) ~[ojdbc7-12.1.0.1.jar!/:12.1.0.1.0]
 at org.springframework.jdbc.support.lob.DefaultLobHandler.getClobAsString(DefaultLobHandler.java:181) ~[spring-jdbc-4.3.5.RELEASE.jar!/:4.3.5.RELEASE]
 at no.gjensidige.bank.datavarehus.debt.DebtApplication.lambda$run$0(DebtApplication.java:63) [classes!/:0.3]
 at no.gjensidige.bank.datavarehus.debt.DebtApplication$$Lambda$7.328FAEA0.mapRow(Unknown Source) ~[na:na]
 at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:93) ~[spring-jdbc-4.3.5.RELEASE.jar!/:4.3.5.RELEASE]
 at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:60) ~[spring-jdbc-4.3.5.RELEASE.jar!/:4.3.5.RELEASE]
 at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:697) ~[spring-jdbc-4.3.5.RELEASE.jar!/:4.3.5.RELEASE]
 at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-4.3.5.RELEASE.jar!/:4.3.5.RELEASE]
 ... 20 common frames omitted



There are 3 actions that can be taken to avoid this bug:

  1. Use the JDBC driver version 12.1.0.2.0 which seems to fix this bug
  2. Install the patch provided in the Oracle support page. (this is only available to customers with a valid Oracle support license). Download and apply Patch 17976703 from Support Portal -> Patches & Updates Section.
  3. Switch back to the OJDBC 6 driver which doesn't have this bug.

Thursday, September 10, 2015

20 tips to reach your goals!

Here is my talk on #JavaZone with the title "20 tips to reach your golas". Hope you benefit from it!

20 tips to reach your goals! from JavaZone on Vimeo.

Tuesday, March 17, 2015

Get Location headers from response in AngularJS and Dropwizard

You have googled and found out that
 headers('Location')  
is the method to use to get the location?
But you always get undefined?
But in your developer tools you can see the Location in the header?

Perhaps you are using CORS (Cross Origin Resource Sharing)?
Enabling CORS only exposes a small number of the available headers by default and if you want more you have to expose them your self.

Use the ExposedHeaders
 // Enable CORS headers  
 FilterRegistration.Dynamic cors = environment.servlets().addFilter("CORS", CrossOriginFilter.class);  
 // Configure CORS parameters  
 cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");  
 cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");  
 cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "OPTIONS,GET,PUT,POST,DELETE,HEAD");  
 cors.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");  
 cors.setInitParameter(CrossOriginFilter.EXPOSED_HEADERS_PARAM, "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin,Location")
 // Add URL mapping  
 cors.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");  

Note the highlighted line. That did the trick!

Thursday, July 19, 2012

Unit testing with JOptionPane

If you are like me, you would still like to be able to unit test your swing applications.

However, its difficult to do this when you have a JOptionPane.showConfirmationDialog, and the user needs to type in yes or no.

In this blog post I will show you how you can accomplish this without needing the user to add anything, or changing your domain code too much.


Lets say you have a simple JFrame you want to test that contains a JOptionPane.


public class SimpleFrame extends JFrame {

  public boolean simpleMethod() {
    int showConfirmDialog = JOptionPane.showConfirmDialog(this, "Can we write test for this?", "Question", JOptionPane.YES_NO_OPTION);
    return showConfirmDialog == JOptionPane.YES_OPTION;
  }
}


Now if you create a JUnit test for this, and run it, you will get a JOptionPane and you need to press the YES or NO button.

To avoid this we can change the code to use an interface and then we can create a mock OptionPane for you tests.



/*
* Note you can add all the methods you use in your application
*/
public interface OptionPane {

      /**
       *  @see JOptionPane#showConfirmDialog(Component, Object, String, int, int);
       */
      int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType);
}


Create three implementations of this interface. One that is delegating to JOptionPane, and the others that will be our mock. One of the mocks will return yes, the other no.


public class DefaultOptionPane implements OptionPane {

      public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
         return JOptionPane.showConfirmDialog(parentComponent,message,title,optionType,messageType);
      }
}

public class YesMockOptionPane extends MockOptionPane {

        @Override
 public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
   return JOptionPane.YES_OPTION;
 }
}


public class NoMockOptionPane extends MockOptionPane {

        @Override
 public int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
   return JOptionPane.NO_OPTION;
 }
}



Now change your application and add the OptionPane


public class SimpleFrame extends JFrame {
  private OptionPane optionPane = DefaultOptionPane();
  
  public boolean simpleMethod() {
    int showConfirmDialog = optionPane.showConfirmDialog(this, "Can we write test for this?", "Question", JOptionPane.YES_NO_OPTION);
    return showConfirmDialog == JOptionPane.YES_OPTION;
  }

  public void setOptionPane(OptionPane o) { this.optionPane = o; }
}


Now in your tests you use the appropriate MockOptionPane.



@Test
public void test() throws Exception {
  SimpleFrame s = new SimpleFrame()
  s.setOptionPane(new YesMockOptionPane());
  Assert.assertTrue(s.simpleMethod());
}

Thursday, June 7, 2012

Always use unique passwords!

Recently we found out that LinkedIn has managed to loose our passwords, so that they encourage people to create new passwords.

And if you are like many others, you have the same password for other sites. With a very easy trick you can guarantee a unique password for all your sites, and its quite easy.
All you have to do is remember an easy formula which you decide.

The formula is:
followed by the first three (or last three) letters in the url. You can combine this to put it before or after your password.

So for LinkedIn for instance it would be: lin or din or lin or din

By doing this, you don't have to login to all the other sites and change your password if your password ever gets compromised.

Tuesday, May 15, 2012

Add custom Font to your Java Swing application

This task was not that trivial as one might think.

It seems that you need to manually set the Font for each of the UI types.
To find out which one that was supported in my System, I did the following:


java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                System.out.println(key.toString());
            }
        }

Which printed out the following:


OptionPane.buttonFont
List.font
TableHeader.font
Panel.font
TextArea.font
ToggleButton.font
ComboBox.font
ScrollPane.font
Spinner.font
RadioButtonMenuItem.font
Slider.font
EditorPane.font
OptionPane.font
ToolBar.font
Tree.font
CheckBoxMenuItem.font
TitledBorder.font
FileChooser.listFont
Table.font
MenuBar.font
PopupMenu.font
Label.font
MenuItem.font
MenuItem.acceleratorFont
TextField.font
TextPane.font
CheckBox.font
ProgressBar.font
FormattedTextField.font
CheckBoxMenuItem.acceleratorFont
Menu.acceleratorFont
ColorChooser.font
Menu.font
PasswordField.font
InternalFrame.titleFont
OptionPane.messageFont
RadioButtonMenuItem.acceleratorFont
Viewport.font
TabbedPane.font
RadioButton.font
ToolTip.font
Button.font


Next, I had to set each one of these values manually:

 final Font TAHOMA_PLAIN_11 = new Font("Tahoma", Font.PLAIN, 11);
 final Font MONOSPACED_PLAIN_13 = new Font("Monospaced", Font.PLAIN, 13);
 final Font SEGOE_UI_PLAIN_12 = new Font("Segoe UI", Font.PLAIN, 12);
 final Font DIALOG_PLAIN_12 = new Font("Dialog", Font.PLAIN, 12);


        UIManager.put("OptionPane.buttonFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("List.font", TAHOMA_PLAIN_11);
        UIManager.put("TableHeader.font", TAHOMA_PLAIN_11);
        UIManager.put("Panel.font", TAHOMA_PLAIN_11);
        UIManager.put("TextArea.font", MONOSPACED_PLAIN_13);
        UIManager.put("ToggleButton.font", TAHOMA_PLAIN_11);
        UIManager.put("ComboBox.font", TAHOMA_PLAIN_11);
        UIManager.put("ScrollPane.font", TAHOMA_PLAIN_11);
        UIManager.put("Spinner.font", TAHOMA_PLAIN_11);
        UIManager.put("RadioButtonMenuItem.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Slider.font", TAHOMA_PLAIN_11);
        UIManager.put("EditorPane.font", TAHOMA_PLAIN_11);
        UIManager.put("OptionPane.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("ToolBar.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Tree.font", TAHOMA_PLAIN_11);
        UIManager.put("CheckBoxMenuItem.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("TitledBorder.font", TAHOMA_PLAIN_11);
        UIManager.put("FileChooser.listFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Table.font", TAHOMA_PLAIN_11);
        UIManager.put("MenuBar.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("PopupMenu.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Label.font", TAHOMA_PLAIN_11);
        UIManager.put("MenuItem.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("MenuItem.acceleratorFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("TextField.font", TAHOMA_PLAIN_11);
        UIManager.put("TextPane.font", TAHOMA_PLAIN_11);
        UIManager.put("CheckBox.font", TAHOMA_PLAIN_11);
        UIManager.put("ProgressBar.font", TAHOMA_PLAIN_11);
        UIManager.put("FormattedTextField.font", TAHOMA_PLAIN_11);
        UIManager.put("CheckBoxMenuItem.acceleratorFont", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("Menu.acceleratorFont", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("ColorChooser.font", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("Menu.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("PasswordField.font", TAHOMA_PLAIN_11);
        UIManager.put("InternalFrame.titleFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("OptionPane.messageFont", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("RadioButtonMenuItem.acceleratorFont", SwingUtils.DIALOG_PLAIN_12);
        UIManager.put("Viewport.font", TAHOMA_PLAIN_11);
        UIManager.put("TabbedPane.font", TAHOMA_PLAIN_11);
        UIManager.put("RadioButton.font", TAHOMA_PLAIN_11);
        UIManager.put("ToolTip.font", SwingUtils.SEGOE_UI_PLAIN_12);
        UIManager.put("Button.font", TAHOMA_PLAIN_11);

Running these different JUnit tests proved it worked:

@Test
    public void testFindJavaDefaultFonts() throws Exception {
        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                Assert.assertEquals("Dialog", ((Font)value).getFamily());
            }
        }
    }
    
    @Test
    public void testFindSystemDefaultFonts() throws Exception {
        final Font font = new Font("Arial", Font.PLAIN, 12);
        UIManager.put("TextField.font", font);
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        UIManager.put("List.font", font);

        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                Assert.assertFalse(key.toString().equals("TextField.font"));
                Assert.assertFalse(key.toString().equals("List.font"));
                
            }
        }
        
        Font font2 = UIManager.getFont("TextField.font");
        Assert.assertNotNull(font2);
        Assert.assertSame(font, font2);

        Font font3 = UIManager.getFont("List.font");
        Assert.assertNotNull(font3);
        Assert.assertSame(font, font3);
    }
    
    @Test
    public void testCustomFonts() throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtils.updateDefaultFonts(); //Where I put all my UIManager.put(...) lines
        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                //This should fail if it comes here since we have set everything manually
                Assert.fail();
            }
        }
        
        Font font = UIManager.getFont("List.font");
        Assert.assertNotNull(font);
        Assert.assertEquals(font.getFamily(), "Tahoma");
    }

If there is an easier way to do this, please do share!

Wednesday, March 28, 2012

WSDL is not part of this compilation. Is this a mistake for wsdl file?

I tried generating a web service client based on wsconsume (which is located in the java distribution folder (bin).

However, upon running the following command:

wsimport.exe -verbose FooBar.wsdl -b Foobar Foobar-jaxb-mapping.xml -s .

I got the following error message:

[ERROR] "file:/C:/workspace/tmp/wsdl/FoobarProxy.wsdl" is not a part of
this compilation. Is this a mistake for "file:/C:/workspace/tmp/FoobarPr
oxy.wsdl"?
line 5 of file:/C:/workspace/tmp/FrontenServiceProxy-jaxb-mapping.xml


I tried googling the error message, and most people suggested that the url had to be changed, and you needed to put #types?schema1 at the end of the url.
However, this wasn't what was wrong for me.

In line 5 of the mapping file, I am referring to the WSDL file. The problem was that in the WSDL file it said that the file should be located in the wsdl folder, and it wasn't. So I created a wsdl folder and put the wsdl file in the correct folder, and voila.

So next time, instead of just blindly googling the error message, I should have read more carefully and looked at the hint "line 5 in the mapping file".
Then I am sure I would have spotted the error!

Lesson learned...

Thursday, September 8, 2011

JavaZone 2011 - My two cents

I just came back from JavaZone 2011, and I have some thoughts.

All in all it was a nice conference. The first day was a little booring. Few really nice talks. However the last day had some really nice talks.

The talks I went to that are worth mentioning are:

CoffeScript
I am not sure what to make of this relatively new scripting language. At first during the presentation, I was very sceptical. The idea is basically to remove/hide out a lot of boiler plate JavaScript code and replace it by magic that adds this at runtime later. It also adds a lot of features on top of JavaScript which made it very cool. That's why I am still on the fence on this one. Can't really decide. However, I don't write that much JavaScript, and thankfully with the help of JQuery, I don't need to. So I don't think I will be looking into this until I really feel that I write a lot of tedious JavaScript.


JavaPosse
Always great! Nuff said!



"Men så hør du da bruker det feil" (dust)

This was a really nice talk from Kåre Nilsen, which basically warned us about Java Frameworks and library hell. He said that we should all think before we add or use a framework, and have a valid reason as to why we use it. We also should know what each library that is bundled with the framework is doing, and we should ask our self if we really need it. Maybe we just can take out the part of the code in the library we are using, and just paste that in our projects.


Play! Framework: to infinity and beyond

I had heard and tested Play! once before this talk, and I found the framework so interesting that I wanted to learn more. And boy am I impressed. This is a framework that you definetly will hear more about in the future. But they did announce that in Play 2, they might break java compatibility and only work with Scala. I don't know what I feel about that, because that kinda forces you to learn "two" things at once. Both Scala and Play!
However, instead of me explaining what Play! is, go check it out!


DISCLAIMER: If your talk is not in this list, its probably just because I didn't attend it. It doesn't mean your session was bad or uninteresting.

Monday, March 21, 2011

API helper / wrapper for ProcessFinder in Hyperic Sigar PTQL

In my previous blog post I wrote that I was going to implement Hyperic Sigar in JODConverter. Well the alpha is done, and you can go try it out here.

When rewriting the code to use the ProcessFinder in Sigar, I found it very cumbersome to work with PTQL, which is the Process Table Query Language for Sigar.

Sigar describes PTQL as:
Hyperic SIGAR provides a mechanism to identify processes called Process Table Query Language. All operating systems assign a unique id (PID) to each running process. However, the PID is a random number that may also change at any point in time when a process is restarted. PTQL uses process attributes that will persist over time to identify a process.

PTQL is string based, and the processFinder takes the ptql as string as parameter.

processFinder.find("State.Name.eq=java"); //Returns all the process id's named java

I have jboss already running and the ps command locally gives me the pid: 28258
> ps -ef | grep jboss
> shervin  28258 27055 20 13:53 pts/1    00:02:36 /usr/lib/jvm/java-6-sun-1.6.0.24/bin/java -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:54988 -javaagent:/home/shervin/opt/jrebel/jrebel.jar -noverify -Dprogram.name=JBossTools: JBoss EAP 5.0 Runtime -Xms512m -Xmx768m -XX:MaxPermSize=512m -Djava.net.preferIPv4Stack=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djava.endorsed.dirs=/home/shervin/opt/jboss/lib/endorsed -Djava.library.path=/usr/lib/jvm/java-6-sun/jre/lib/i386/server:/usr/lib/jvm/jdk1.5.0_22/jre/lib/i386:/usr/lib/jvm/jdk1.5.0_22/jre/../lib/i386:/usr/lib/jvm/jdk1.5.0_22/jre/lib/i386/client:/usr/lib/jvm/jdk1.5.0_22/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/ure/lib/:/home/shervin/lib/lib-src/hyperic-sigar-1.6.4-src/bindings/java/sigar-bin/lib -Dfile.encoding=UTF-8 -classpath /home/shervin/opt/jboss/bin/run.jar org.jboss.Main --configuration=default -b localhost
When running the sigar shell (java -jar sigar.jar) and writing
sigar> pargs 28258
It gives the output (the numbers are arguments)
pid=28258
exe=/usr/lib/jvm/java-6-sun-1.6.0.24/jre/bin/java
cwd=/home/shervin/opt/jboss-eap-5.1/jboss-as/bin
   0=>/usr/lib/jvm/java-6-sun-1.6.0.24/bin/java<=
   1=>-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:54988<=
   2=>-javaagent:/home/shervin/opt/jrebel/jrebel.jar<=
   3=>-noverify<=
   4=>-Dprogram.name=JBossTools: JBoss EAP 5.0 Runtime<=
   5=>-Xms512m<=
   6=>-Xmx768m<=
   7=>-XX:MaxPermSize=512m<=
   8=>-Djava.net.preferIPv4Stack=true<=
   9=>-Dsun.rmi.dgc.client.gcInterval=3600000<=
   10=>-Dsun.rmi.dgc.server.gcInterval=3600000<=
   11=>-Djava.endorsed.dirs=/home/shervin/opt/jboss/lib/endorsed<=
   12=>-Djava.library.path=/usr/lib/jvm/java-6-sun/jre/lib/i386/server:/usr/lib/jvm/jdk1.5.0_22/jre/lib/i386:/usr/lib/jvm/jdk1.5.0_22/jre/../lib/i386:/usr/lib/jvm/jdk1.5.0_22/jre/lib/i386/client:/usr/lib/jvm/jdk1.5.0_22/jre/lib/i386:/usr/lib/xulrunner-addons:/usr/lib/xulrunner-addons:/usr/lib/ure/lib/:/home/shervin/lib/lib-src/hyperic-sigar-1.6.4-src/bindings/java/sigar-bin/lib<=
   13=>-Dfile.encoding=UTF-8<=
   14=>-classpath<=
   15=>/home/shervin/opt/jboss/bin/run.jar<=
   16=>org.jboss.Main<=
   17=>--configuration=default<=
   18=>-b<=
   19=>localhost<=


Now imagine you want to create a more advanced query which takes the one or more of the 19 arguments the java process takes.

The query would look something like this:

processFinder.find("State.Name.ct=java,Args.5.eq=-Xms512m,Args.6.eq=-Xmx768m,Args.7.re=.*MaxPermSize=512m,Args.17.ct=configuration=default,Args.19.eq=localhost");
or you are building the query based on variables which you must retrieve somewhere else
public void testArgs() throws Exception {
        String state_name= "State.Name.ct=java";
        String arg5 = ",Args.5.eq=-Xms512m";
        String arg6 = ",Args.6.eq=-Xmx768m";
        String arg7 = ",Args.7.re=.*MaxPermSize=512m";
        String arg17 = ",Args.17.ct=configuration=default";
        String arg19 = ",Args.19.eq=localhost"; 
        ProcessFinder processFinder = new ProcessFinder(new Sigar());
        processFinder.find(state_name + arg5 + arg6+ arg7 + arg17 + arg19);
    }
It doesn't take a genius to see that it is easy to make mistakes. Hence the more type-safe way of creating PTQL queries.

SimplePTQL


I took the liberty to create a helper/wrapper class for ProcessFinder which tries to eliminate some of the errors you can do.
For instance, instead of the code above, you would write the following:

SimplePTQL ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.CT(), "java")
                    .addArgs(5, SimplePTQL.EQ(), "-Xms512m", Strategy.NOT_ESCAPE)
                    .addArgs(6, SimplePTQL.EQ(), "-Xmx768m", Strategy.NOT_ESCAPE)
                    .addArgs(7, SimplePTQL.RE(), ".*MaxPermSize=512m", Strategy.ESCAPE)
                    .addArgs(17, SimplePTQL.CT(), "configuration=default", Strategy.NOT_ESCAPE)
                    .addArgs(19, SimplePTQL.EQ(), "localhost", Strategy.NOT_ESCAPE)
                    .createQuery();
        
ProcessFinder processFinder = new ProcessFinder(new Sigar());
processFinder.find(ptql.getQuery());
The constructor of the Builder takes the first part of the query as parameters and you can optionally add arguments. You can escape the input which regular expressions should do. It will basically replace all commas (,) with period (.), this because the find method doesn't like commas in the searchValue inside a regular expression.
/**
     * This method will escape Comma (,) to Period (.)
     * Because the PTQL cannot escape those correctly, so we will use '.' in regular expression.
     * We also have to remove \Q and \E because they are not correctly interpreted as literal characters
     * 
     * @param s - The string you want to espace
     * 
     * NB: Note that you should only espace the value of the PTQL, not the query it self
     * ie: State.Name.ct=pipe,name=office1 should be converted to State.Name.ct=pipe.name.office1
     * @return - The escaped string
     */
    public static String escapePTQLForRegex(String s) {
        return s.replaceAll(",", ".").replaceAll("\\\\Q|\\\\E", "");
    }

The latest version of SimplePTQL can be found here

And the source (subject to change, see the former link for latest version) is:
At the time of writing, the SimplePTQL class does not support Env and Modules

//
// JODConverter - Java OpenDocument Converter
// Copyright 2011 Art of Solving Ltd
// Copyright 2004-2011 Mirko Nasato
//
// JODConverter is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// JODConverter is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with JODConverter.  If not, see
// <http://www.gnu.org/licenses/>.
//
package org.artofsolving.jodconverter.sigar;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.artofsolving.jodconverter.util.PlatformUtils;

import com.google.common.base.Preconditions;

/**
 * A simplified PTQL so to simplify the query building and make it less error prone and typesafe. 
 * 
 * See more information http://support.hyperic.com/display/SIGAR/PTQL
 * 
 * @author Shervin Asgari - <a href="mailto:shervin@asgari.no">shervin@asgari.no</a>
 */
public class SimplePTQL {
    private final Operator operator;
    private final Attribute attribute;
    private final String searchValue;
    private final String args;
    private final Strategy strategy;
    
    public enum Strategy {
        ESCAPE, NOT_ESCAPE
    }

    private SimplePTQL(Attribute attribute, Operator operator, String searchValue, String args, Strategy strategy) {
        this.attribute = attribute;
        this.operator = operator;
        this.searchValue = searchValue;
        this.args = args;
        this.strategy = strategy;
    }
    
    public static class Builder {
        private final Operator operator;
        private final Attribute attribute;
        private final String searchValue;
        
        private Strategy strategy = Strategy.NOT_ESCAPE; 
        private StringBuilder args = new StringBuilder(""); //Default blank
        
        public Builder(Attribute attribute, Operator operator, String searchValue) {
            this.attribute = attribute;
            this.operator = operator;
            this.searchValue = searchValue;
        }

        public Builder addArgs(int argument, Operator operator, String searchValue, Strategy strategy) {
            Preconditions.checkNotNull(searchValue);
            
            if(strategy == Strategy.ESCAPE) {
                args.append(",Args." + String.valueOf(argument) + "." + operator.toString() + PlatformUtils.escapePTQLForRegex(searchValue));
            } else {
                Pattern pattern = Pattern.compile(",|=");
                Matcher matcher = pattern.matcher(searchValue);
                Preconditions.checkArgument(!matcher.find(), "searchValue cannot contain comma or equals sign. Either set Strategy.ESCAPE or remove it from the search value");
                args.append(",Args." + String.valueOf(argument) + "." + operator.toString() + searchValue);
            }
            
            return this;
        }
        
        public Builder setStrategy(Strategy strategy) {
            this.strategy = strategy;
            return this;
        }

        public SimplePTQL createQuery() {
            return new SimplePTQL(attribute, operator, searchValue, args.toString(), strategy);
        }
    }
    
    /**
     * Returns the entiry PTQL query.
     * ie: 
     * <ul>
     * <li>State.Name.eq=java</li>
     * <li>Pid.Pid.eq=4245</li>
     * <li>State.Name.re=^(https?d.*|[Aa]pache2?)$</li>
     * </ul>
     * @return
     */
    public String getQuery() {
        return attribute.toString() + operator.toString() + (strategy == Strategy.ESCAPE ? PlatformUtils.escapePTQLForRegex(searchValue): searchValue) + args;
    }    

    private interface Operator {
        String toString();
    }

    private interface Attribute {
        String toString();
    }

    /**
     * Equal to value
     */
    public static Operator EQ() {
        return new Operator() {
            @Override
            public String toString() {
                return "eq=";
            }
        };
    }

    /**
     * Not Equal to value
     */
    public static Operator NE() {
        return new Operator() {
            @Override
            public String toString() {
                return "ne";
            }
        };
    }

    /**
     * Ends with value
     */
    public static Operator EW() {
        return new Operator() {
            @Override
            public String toString() {
                return "ew=";
            }
        };
    }

    /**
     * Starts with value
     */
    public static Operator SW() {
        return new Operator() {
            @Override
            public String toString() {
                return "sw=";
            }
        };
    }

    /**
     * Contains value (substring)
     */
    public static Operator CT() {
        return new Operator() {
            @Override
            public String toString() {
                return "ct=";
            }
        };
    }

    /**
     * Regular expression value matches
     */
    public static Operator RE() {
        return new Operator() {
            @Override
            public String toString() {
                return "re=";
            }
        };
    }

    /**
     * <i>Only for numeric value<i> Greater than value
     */
    public static Operator GT() {
        return new Operator() {
            @Override
            public String toString() {
                return "gt=";
            }
        };
    }

    /**
     * <i>Only for numeric value<i> Greater than or equal value
     */
    public static Operator GE() {
        return new Operator() {
            @Override
            public String toString() {
                return "ge=";
            }
        };
    }

    /**
     * <i>Only for numeric value<i> Less than value
     */
    public static Operator LT() {
        return new Operator() {
            @Override
            public String toString() {
                return "lt=";
            }
        };
    }

    /**
     * <i>Only for numeric value<i> Less than value or equal value
     */
    public static Operator LE() {
        return new Operator() {
            @Override
            public String toString() {
                return "le=";
            }
        };
    }

    /**
     * The Process ID
     */
    public static Attribute PID_PID() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Pid.Pid.";
            }
        };
    }

    /**
     * File containing the process ID
     */
    public static Attribute PID_PIDFILE() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Pid.PidFile.";
            }
        };
    }

    /**
     * Windows Service name used to pid from the service manager
     */
    public static Attribute PID_SERVICE() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Pid.Service.";
            }
        };
    }

    /**
     * Base name of the process executable
     */
    public static Attribute STATE_NAME() {
        return new Attribute() {
            @Override
            public String toString() {
                return "State.Name.";
            }
        };
    }

    /**
     * User Name of the process owner
     */
    public static Attribute CREDNAME_USER() {
        return new Attribute() {
            @Override
            public String toString() {
                return "CredName.User.";
            }
        };
    }

    /**
     * Group Name of the process owner
     */
    public static Attribute CREDNAME_GROUP() {
        return new Attribute() {
            @Override
            public String toString() {
                return "CredName.Group.";
            }
        };
    }

    /**
     * User ID of the process owner
     */
    public static Attribute CRED_UID() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Cred.Uid.";
            }
        };
    }

    /**
     * Group ID of the process owner
     */
    public static Attribute CRED_GID() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Cred.Gid.";
            }
        };
    }

    /**
     * Effective User ID of the process owner
     */
    public static Attribute CRED_EUID() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Cred.Euid.";
            }
        };
    }

    /**
     * Effective Group ID of the process owner
     */
    public static Attribute CRED_EGID() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Cred.Egid.";
            }
        };
    }

    /**
     * Full path name of the process executable
     */
    public static Attribute EXE_NAME() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Exe.Name.";
            }
        };
    }

    /**
     * Current Working Directory of the process
     */
    public static Attribute EXE_CWD() {
        return new Attribute() {
            @Override
            public String toString() {
                return "Exe.Cwd.";
            }
        };
    }
}

The unit test for this class can also be found here

Or the source (at the time of writing)
//
// JODConverter - Java OpenDocument Converter
// Copyright 2011 Art of Solving Ltd
// Copyright 2004-2011 Mirko Nasato
//
// JODConverter is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// JODConverter is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with JODConverter.  If not, see
// <http://www.gnu.org/licenses/>.
//
package org.artofsolving.jodconverter.sigar;

import java.util.List;

import org.artofsolving.jodconverter.process.ProcessManager;
import org.artofsolving.jodconverter.process.SigarProcessManager;
import org.artofsolving.jodconverter.sigar.SimplePTQL.Strategy;
import org.artofsolving.jodconverter.util.PlatformUtils;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.Test;

@Test
public class SimplePTQLTest {
    
    public void simpleQuery() throws Exception {
        ProcessManager spm = new SigarProcessManager();
        SimplePTQL ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.EQ(), "java").createQuery();
        Assert.assertEquals(ptql.getQuery(), "State.Name.eq=java");
        
        List<Long> find = spm.find(ptql);
        Assert.assertTrue(find.size() > 0);
        
        if(find.size() > 1) {
            try {
                spm.findSingle(ptql);
                Assert.fail("Should not reach here, should get exception");
            } catch(NonUniqueResultException nre) {
                //More than one results where found
            }    
        }
        
        ptql = new SimplePTQL.Builder(SimplePTQL.PID_PID(), SimplePTQL.EQ(), String.valueOf(find.get(0))).createQuery();
        Long findSingle = spm.findSingle(ptql);
        Assert.assertEquals(findSingle, find.get(0));
        
        ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.EQ(), "Hopefully There is no Process called this").createQuery();
        List<Long> find2 = spm.find(ptql);
        Assert.assertTrue(find2.size() == 0);
        
        ptql = new SimplePTQL.Builder(SimplePTQL.PID_PID(), SimplePTQL.GT(), "1").createQuery();
        List<Long> find3 = spm.find(ptql);
        Assert.assertTrue(find3.size() > 1);
    }
    
    public void args() throws Exception {
        SimplePTQL ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.RE(), "office.*")
        .addArgs(1, SimplePTQL.RE(), "\\Qpipe,name,office1\\E", Strategy.ESCAPE)
        .createQuery();
        Assert.assertEquals(ptql.getQuery(), "State.Name.re=office.*,Args.1.re=pipe.name.office1");
        
        ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.EQ(), "office.*")
        .addArgs(1, SimplePTQL.RE(), "\\Qpipe,name,office1\\E", Strategy.ESCAPE)
        .addArgs(2, SimplePTQL.EQ(), "\\Qpipe,name=office2\\E", Strategy.ESCAPE)
        .setStrategy(Strategy.ESCAPE)
        .createQuery();
        
        Assert.assertEquals(ptql.getQuery(), "State.Name.eq=office.*,Args.1.re=pipe.name.office1,Args.2.eq=pipe.name=office2");
        
        try {
            ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.EQ(), "office.*")
            .addArgs(1, SimplePTQL.RE(), "\\Qpipe,name,office1\\E", Strategy.ESCAPE)
            .addArgs(2, SimplePTQL.EQ(), "\\Qpipe,name,office2\\E", Strategy.NOT_ESCAPE)
            .createQuery();
        
            Assert.fail("Method should have thrown IllegalArgumentException");
        } catch(IllegalArgumentException ex) {}
    }

    public void realArguments() throws Exception {
        if (PlatformUtils.isWindows()) {
            throw new SkipException("Sleep only works on unix");
        }
        
        Process process = new ProcessBuilder("sleep", "60s").start();
        Assert.assertNotNull(process);
        
        SimplePTQL ptql = new SimplePTQL.Builder(SimplePTQL.STATE_NAME(), SimplePTQL.EQ(), "sleep")
                            .addArgs(1, SimplePTQL.EQ(), "60s", Strategy.NOT_ESCAPE).createQuery();
        
        
        ProcessManager spm = new SigarProcessManager();
        Long findSingle = spm.findSingle(ptql);
        Assert.assertTrue(findSingle > 0L);
        
        spm.kill(findSingle, 9);
        Assert.assertEquals(spm.findSingle(ptql).longValue(), 0L);
    }
}

Testing Prettify

This is a test of Prettify
public class NonUniqueResultException extends Exception {

 private static final long serialVersionUID = -7862607833306958651L;
 
 public NonUniqueResultException(String string) {
        super(string);
    }
}

Monday, March 14, 2011

Commit access to JodConverter

I have gotten commit access to jodconverter 3 so that I can work on some issues.

The first issue I am going to work on is refactoring the code to use Sigar.

Sigar "provides a portable interface for gathering system information" with native libraries for many OSes and it has an Apache License Version 2.

This is my first commit access to an open source project. Looking forward to contribute.


 

Monday, March 7, 2011

Creating an efficient memory based cache

If you find your self in the situation where you are creating global maps as cache, then you have to stop and rethink.
Global maps are prone to memory leaks

You should instead consider using soft reference and WeakHashMap or what I prefer, the MapMaker of Google Guava.

In this blog post, I will describe an efficient way of creating a map based cache. This can can either be stored in the session, application, or your existing cache.

Weak References
- What are weak references?

Weak reference basically means that the garbage collector can come and remove it when it is no longer in use. You have no guarantee that whatever you put in the map, will actually be around when you try to get it.
The reason why that is so useful is if you don't want to (or cannot afford to) retain an object indefinitely in memory.


Consider the following use case: You need to associate information with classes. Now, since you are running in an environment, where classes might get reloaded (say, a Tomcat, or OSGi environment), you want the garbage collector to be able to reclaim old versions of a class as soon as it deems safe to do so.

An initial attempt to implement this, might look like something like this:
 class ClassAssociation {  
   private final IdentityHashMap<Class<?>,MyMetaData> cache = new ...;  
 }  

The problem here is; this would keep all classes in the cache member forever (or at least, unless they are manually removed), forcing the garbage collector to retain them indefinitely, including everything referenced from the class (static member values, class loader information, etc).

By using weak references, the garbage collector can reclaim old version of the class as soon as no other references to it (usually instances) exist.
On the other hand, as long as such references exist, the value is guaranteed to also be reachable from the weak reference object, and thus, is a valid key in the cache table.

MapMaker FTW!

The thing about MapMaker is that there are many options for the kind of map you build, which enables those maps to serve many different purposes.
With the MapMaker you can choose between weak keys or weak values.

  • Soft values are useful for caching, as you can cache values in the map without worrying about running out of memory since the system is free to evict entries from the cache if it needs memory.
  • You can choose to have entries expire after a certain amount of time. This is also useful for caching, since you may want certain data cached for a specific period of time before doing an expensive operation to update it.
  • One of my favorite things is making a computing map. A computing map uses a Function to automatically retrieve the value associated with a given key if it isn't already in the map. This combines well with soft values and/or expiration times. After an entry is evicted by the map (due to memory demand or expiration), the next time the value associated with that key is requested it will automatically be retrieved and cached in the map once more.
Consider this example:
You have an expensive computation or query which you want to cache for performance gains. You store the value in a map with an id as key which you will use to retrieve your values.
Normally you would store these values in a regular HashMap and store the hashmap in the cache, session or application. Now we have seen that this is generally not a good idea, since it will consume a lot of memory.
It is in these situations the MapMaker shines!
Lets say you have a list of Tasks for each User. 
You would normally query the tasks like this:
Map<User,List<Task>> cache = new HashMap<User,List<Task>>(); //the global cache defined somewhere
 if(cache.get(user) == null) {
   List<Task> userTasks = getTasksForUser(user); // perform an intensive computation/query which we want to cache
   cache.put(user, userTasks);
 }
 return cache.get(user);

If you want to rewrite this to use a Computing MapMaker you would write like this:
ConcurrentMap<String, List<Task>> cache = ...// Get the cache
    if(cache != null) {
      //If the tasks have been garbage collected, the function is applied, and you get the tasks 
      return cache.get(user);
    } else {
      ConcurrentMap<String, List<Task>> cache = new MapMaker().softValues().expireAfterWrite(2L, TimeUnit.HOURS)
        .makeComputingMap(new Function<User, List<Task>>() {
        @Override
        public List<Task> apply(User user) {
          return getTasksForUser(user); // perform an intensive computation/query which we want to cache 
        }
      });
      
      cache.put(user, getTasksForUser(user));
      return cache.get(user);
    }

Here we have created a ConcurrentMap with weak values, which will be garbage collected in two hours. If the tasks have been garbage collected and the user is retrieving the tasks, the function is applied, and you get the tasks automatically, and put it back in the cache for another two hours.

Simple and great!

Tuesday, January 11, 2011

Remember that ordinal parameters are 1-based in hibernate

I got this strange exception java.lang.IndexOutOfBoundsException: Remember that ordinal parameters are 1-based!

When running this query in hibernate/jpa

jbpmContext.getSession().createQuery("select pi from org.jbpm.graph.exe.ProcessInstance pi where pi.id = ?1 or pi.id = ?2 or pi.id =?3")
            .setParameter(1, 9L).setParameter(2, 10L).setParameter(3, 11L).list();


Which is very strange. The reason why hibernate cannot figure this out is because the ejb 3 form
("?1")
is interpreted as a named parameter! So here we actually have to write it as
.setParameter("1", 9L)
instead, or change the query and only type ? like so.

jbpmContext.getSession().createQuery("select
 pi from org.jbpm.graph.exe.ProcessInstance pi where pi.id = ? or pi.id
 = ? or pi.id =?").setParameter(0, 9L).setParameter(1, 10L).setParameter(2, 11L).list();


The message about "1-based" is misleading here. It refers to the fact the the metadata for parameters is actually indexed by their sql positions (which is 1-based).

Good to know!

Tuesday, December 14, 2010

How to reduce your re-deployment time

Are you tired of always waiting for re-deployment whenever you change something during development?

I sure am! Recently it has even become worse, because my Enterprise JBoss Application Server takes around 4 minutes to boot, and it is not uncommon that I redeploy the application up to 20 - 30 times during one day.
That's already  80 - 120 minutes per day accumulated that I just have to wait for the application to start, and what's worse, many times I am in the flow, and really concentrated on the task at hand, then I have to redeploy, and I will unset my mind and start browsing some emails, forums, etc and totally loose my flow. It's hard to get back in that mindset again.

Wouldn't it be awesome that whenever you saved a change in your IDE, that it would instantly be picked up by the application server, and reloaded? Why do we need to reload the entire application each time? It doesn't make sense.


JRebel to the rescue!


I have known about JRebel for some time, and I knew about its awesomeness. However, when I tried to install it a few years back, it was really tedious and error prone, and I couldn't really get it working correctly. But recently JRebel has been shipped with a new configuration wizard which basically does everything for you. It took next to no time to install it and get it working, and already it is saving me a lot of time. 

JRebel is just awesome, and every Java developer should (read must) use it!

Friday, December 3, 2010

Moments when you should sense danger in Chess

1. There has been a change in the pawn structure. Your opponent has 8 and you don't have any.

2. Your opponent begins to throw pawns at your eyes.

3. You have a position won but your opponent has a gun.

4. The Director tells you not to bother turning in your scoresheet after the game.

5. Before the game begins you notice your opponents 1st initials are 'GM'.

6. After completing your development you sense your opponent playing the endgame.

7. Just as you make your opening move your opponent announces mate in 11.

8. You don't control any squares at all.

9. Your draw offer sends all the people watching your game into uncontrollable laughter.

10. Your opponent has 3 bishops.

11. Your opponent slaps his head and cries "Noooo what a mistake!!", then few moves later, you are down a queen.

12. You announce forced mate in 7 by sacking two pieces, then you resign after the 8'th move.

13. You crush your opponent on the chess board, but the opponent crushes you with the chess board.

Thursday, November 4, 2010

How to add RichFaces to your existing jQuery enabled site

As of Richfaces 3.0, jQuery has been inbuilt. If you don't know what jQuery is, you can watch a video about it here.

We have been using jQuery separately in our project, but wanted to add some Richfaces components. Now since Richfaces is using jQuery, there is a conflict that appears.

They both use $ as their main function name. That might brake the behaviour, and you might see error messages in your web browser, such as:

this.focusKeeper.observe is not a function

[Break on this error] Richfaces.ListBase.ASC="acs";Richfaces...ner('load',this.imagesOnLoad,true);}}
ListBase.js (line 4)

and
element.dispatchEvent is not a function

[Break on this error] event.eventName=eventName;event.memo=m...nt.fireEvent(event.eventType,event);}
3_3_1....eScript (line 265)

To resolve such cases jQuery introduces the .noConflict() function.

To use the jQuery that is bundled in RichFaces, you have to load the correct script.

<a:loadScript src="resource://jquery.js"/>

Then you can assign jQuery to $j for convenience. Add the following in your javascript code:
$j = jQuery.noConflict();
Then you have to replace all your former $() with $j() or the equivalents $. $[] and function($) with $j. $j[] and function($j)

Your new code now may look something like this:

function showMessages() {
  $j("div#messagetextPanel").fadeIn("fast");
}

And your Richfaces component will display and work without any problems! Thats it!

Thursday, October 7, 2010

Why you should NOT use the Seam Application Framework

NOT TO BE CONFUSED WITH SEAM, WHICH IS AWESOME!

I am a very experienced Seam user. I have used Seam daily now for almost 3 years.

In this blog post, I will try to convince you to avoid the Seam Application Framework, and truly understanding when it should be used, and when it shouldn't.

What is Seam Application Framework?
 Seam in Action says: "Seam is an application framework for Java EE. It provides a container that manages components and leverage's those components to tie the layers of an enterprise Java application together. Nestled within the Seam code base lies a handful of classes that comprise the Seam Application Framework. This "framework within a framework" is a specialized collection of component templates that effortlessly blanket the programming requirements of garden-variety web applications. Such tasks include performing create,read,update and delete (CRUD) operations on entity instances;querying for data; and developing JSF page controllers. You may be hesitant to give this spattering of classes much notice, but I assure you that they'll carry you a long way."

According to Dan Allen, he is encouraging you to check this "framework within a framework" out. However, though he is right, that there are benefits with this framework, I will prove that there are far more disadvantages than advantages and that he should have rewritten the last line to include that the framework is only really useful for small CRUD applications, and not something to leverage and build upon.

Way too many people are using this framework
If you have a look at the Seamframework.org forums, you will see tons of questions from people having problems and issues with the Seam Application Framework. Either a lot of people are developing small CRUD based applications, or there is a growing misconception about Seam and Seam Application Framework.

There are many people that are confused about this notion. They think that Seam == Seam Application Framework, and that they have to use it. And who can blame them? The seam-gen utility is a fantastic way of getting you quickly started. However, I have always encouraged people to only use the create-project and generate-entities part of the utility, and not the generate-ui part of it, this because most people I have talked with, are not developing small CRUD based applications, but rather medium sized enterprise apps, but since you get the Seam Application Framework for free, it is quite easy to extend and use it.

PROS
I did mention that there are a few pro's about using this framework, but it depends on people truly understanding the inner workings of the framework.
  • Quick to get started
  • Excellent for small models,presentation and proof of concept
  • Great in-built pagination of lists
  • Can @Override the methods and inherit classes to extend/tweak upon the framework
If you want to visualize for your customer, boss or client, an application as a proof of concept, then it is very easy to do this using the framework. In this case you don't care so much about performance and inherited abstraction layers, but rather how quickly you can get something visual and up and running.

I also want to note, before I get spammed with comments about how wrong I am, that you can if you know what you are doing, speed things up. Details can be found here. However, this again requires some knowledge about Seam.

CONS
  • Its a bit slower
  • More abstraction which might not suit your requirements
  • More difficult to use than the plain components. You need to "learn a new framework as well as Seam"

Consider this very very simple example. We have a SystemLog, and we want to display the log.


@Entity
public class SystemLog {

    @Getter @Setter
    @Id
    @GeneratedValue
    private Long id;

    @Getter @Setter
    @NotEmpty
    private String description;

    @Getter @Setter
    @Column
    private String category;

    @Getter @Setter
    @Temporal(TemporalType.TIMESTAMP)
    private Date date;

    @Getter @Setter
    @Column
    private String username;

    @Getter @Setter
    @Column
    private String organization;

    @Getter @Setter
    @Column(length = 1024)
    @Lob
    private String details;

    @Getter @Setter
    @Column
    private String severity;

    @Getter @Setter
    @Column
    private String ipAddress;

}

We have an action class that used the EntityQuery of the Seam Application Framework.


@Name("systemLogQuery")
@MeasureCalls
public class SystemLogQuery extends EntityQuery<SystemLog> {
  @Logger Log log;
  
  @Override
  public String getEjbql() {
    return "select sys from SystemLog sys";
  }
  
  @Override
  public List<SystemLog> getResultList() {
    log.warn("Inside systemlog query getResultList");
    return super.getResultList();
  }

}

And this normal (non tweaked) seam component.


@Name("systemLogNormal")
@MeasureCalls
public class SystemLogNormal {

  @Logger Log log;
  
  @In EntityManager entityManager;
  
  List<SystemLog> result;
  
  public List<SystemLog> getResultList() {
    log.warn("Inside systemLog normal resultList");
    if(result == null)
      this.result = entityManager.createQuery("FROM SystemLog").getResultList();
     
    return result;
  }
}


And a simple data table iterating over the elements

<h:dataTable value="#{systemLogNormal.resultList}" var="sys">
    <h:column>
      <f:facet name="header">
        description
      </f:facet>
      #{sys.description}
    </h:column>
</h:dataTable>

Now at first glance, I think the normal seam component is much cleaner and easier to read. Remember, this is a very simple example. In a normal real life example you would have extended the component to be much more complicated, and if you want to do this using the existing framework, you will need to find out what the framework already is doing, and correctly override those methods. Otherwise, you risk of doing the same thing twice.

If you ask your self what the @MeasureCalls annotation is doing, then have a look here, and scroll down to the second example. But a short description is that it measures the time of execution for a method.

When you run this example using the SystemLogNormal and the Query component, they are pretty much equal. However, you will see that the Query component is also calling the validate() method, which takes a few milliseconds to execute.
It is correct that a few milliseconds is hardly anything noteworthy, however this is a very very simple example, with very little data, and in a more realistic scenario, the number could possibly be different. Either way, the overall time difference is that the EntityQuery object is a few milliseconds slower.

13:58:24,224 WARN  [SystemLogQuery] Inside systemlog query getResultList
13:58:24,228 INFO  [STDOUT] Hibernate: 
    select
        systemlog0_.id as id0_,
        systemlog0_.category as category0_,
        systemlog0_.date as date0_,
        systemlog0_.description as descript4_0_,
        systemlog0_.details as details0_,
        systemlog0_.ipAddress as ipAddress0_,
        systemlog0_.organization as organiza7_0_,
        systemlog0_.severity as severity0_,
        systemlog0_.username as username0_ 
    from
        SystemLog systemlog0_
13:58:24,239 WARN  [SystemLogQuery] Inside systemlog query getResultList
13:58:24,257 WARN  [SystemLogQuery] Inside systemlog query getResultList
13:58:24,334 INFO  [TimingFilter] 2.201607 ms   1   EntityQuery.validate()
13:58:24,334 INFO  [TimingFilter] 15.967185 ms   3   SystemLogQuery.getResultList()



13:58:31,647 WARN  [SystemLogNormal] Inside systemLog normal resultList
13:58:31,648 INFO  [STDOUT] Hibernate: 
    select
        systemlog0_.id as id0_,
        systemlog0_.category as category0_,
        systemlog0_.date as date0_,
        systemlog0_.description as descript4_0_,
        systemlog0_.details as details0_,
        systemlog0_.ipAddress as ipAddress0_,
        systemlog0_.organization as organiza7_0_,
        systemlog0_.severity as severity0_,
        systemlog0_.username as username0_ 
    from
        SystemLog systemlog0_
13:58:31,662 WARN  [SystemLogNormal] Inside systemLog normal resultList
13:58:31,678 WARN  [SystemLogNormal] Inside systemLog normal resultList
13:58:31,712 INFO  [TimingFilter] 15.740409 ms   3   SystemLogNormal.getResultList()


As you can see, we get three outputs of "Inside systemlog". Really there should only be one. So adding a @Factory annotation on top of it will do the trick for both versions.

Then there are the Home and Query objects, with methods like isIdDefined(), clearDirty(), isManaged(), getEjbql(), RESTRICTIONS and more confusing methods which you need to read the documentation to understand.

Conclusion

In my opinion, for first time users who are learning Seam, it is more than enough to understand and learn the framework, instead of in addition learning "a framework inside a framework". The seam-gen utility is sort of a devil in disguise. Its great for a shortcut to get started, but its evil in disguise for new comers.

Newbies think Seam Application Framework is Seam, and all components they create must extends the Home, List or Query objects. Its confusing, and in my opinion they should avoid using it unless they know all the ins and out of both Seam and the application framework.

So if you are new to Seam, start by looking at the Seam examples that comes with the bundle. They are far better to use, than generating the UI with seam-gen and going from there.

Friday, September 17, 2010

Devoxx versus JavaOne

Devoxx or JavaOne?
Thats the question...

It's really not that difficult to choose.
If you are based in Europe (as our company is), then you will for sure get more value for your money attending Devoxx instead of JavaOne.

However, if you look at a technical perspective, then still Devoxx comes on top in my opinion. The opening talk is by Mark Reinhold and one of the last talks, "Java state of the Union" is by no other than James Gosling. (I don't even need to link to him, every Java developer should know who he is), and there are tons of fameous speakers: Brian Goetz, The JavaPosse, Heinz Kabutz, Richard Bair and Roberto Chinnici just to name a few. (No pun intented for the others I didn't mention).

So it shouldn't come as a big surprise that I am also attending Devoxx. If you are going, lemme know and we can hook up!

Sunday, September 12, 2010

Java 7, yet another delay

Mark Reinhold has published a blog stating what has been painfully obvious to everyone following the JDK 7 development: It will yet again be delayed until mid 2012(!)

Mark is further saying that there is an alternative which they are considering, and that "is to take everything we have now, test and stabilize it, and ship that as JDK 7. We could then finish Lambda, Jigsaw, the rest of Coin, and maybe a few additional key features in a JDK 8 release which would ship fairly soon thereafter."

I couldn't agree more. The community has waited too long for Java 7 to come out. There are so many problems in the current Java version, that makes people look around for alternatives in the Java Virtual Machine.
I am certain that if Java 7 will be delayed for yet two more years, then most people by that time will move to other languages such as Scala and Grails, which doesn't have the problems Java has today. 

So, to sum up. Oracle has my vote to ship whatever they have now, and then come with the rest of it with JDK 8.

Monday, August 2, 2010

Migrating from JODConverter 2 to JODConverter 3 and converting PDF to PDF/A

In the previous posting I showed you how you could automate conversions of documents to PDF & PDF/A using JODConverter 2.  

JODConverter 3.0.beta has been out for some time, and even though it is still beta, it is very stable. Maybe even more stable than JODConverter 2.

In this blog posting I will highlight the benefits of JODConverter 3 compared to its predecessor and show you how you can modify your code to create PDF/A documents with JODConverter 3.  
To be able to convert an existing PDF document to PDF/A in OpenOffice.org, you will need to install Sun PDF Import extension!

JODConverter 2 versus 3
JODConverter 3 still uses OpenOffice.org to perform its conversion. It is still a wrapper to the OOo API. It is only a complete rewrite of the JODConverter core library which is much cleaner and easier to use.

Whats new? 
  • No more init script(!) 
    • You don't have to manually start OpenOffice.org as a service anymore. This will be handled automatic.
    • You can even create multiple processes which is useful for multi-core CPU's. Best practise is one process for each CPU core.
  • Automatically restart an OOo instance if it crashes.
    • If for some reason your process crashes, JODConverter will detect this, and restart the process automatic. This was a hassle with JODConverter 2, as you needed to manually do this in Linux.
  • Abort conversions that take too long (according to a configurable timeout parameter)
  • Automatically restart an OOo instance after n conversions (workaround for OOo memory leaks)
Additionally the new architecture will make it easier to use the core JODConverter classes as a generic framework for working with OOo - not just limited to document conversions.
I am sure there will be more features when JODConverter 3 goes final.

Configuration

All you need to do do is point your OpenOffice.org installation to the OfficeManager, and you are good to go.

OfficeManager officeManager = new DefaultOfficeManagerConfiguration()
        .setOfficeHome("/usr/lib/openoffice")
        .buildOfficeManager().start();

This manager will use the default settings for Task Queue Timeout, Task Execution Timeout, Port Number etc but you can easily change them

OfficeManager officeManager = new DefaultOfficeManagerConfiguration()
        .setOfficeHome("/usr/lib/openoffice")
        .setTaskExecutionTimeout(240000L)
        .setTaskQueueTimeout(60000L)
        .buildOfficeManager().start();

If you want to utilize piping (Recommended is one process per CPU-core), you will need to set VM argument and point java.library.path to the location of $URE_LIB which on my Ubuntu machine is /usr/lib/ure/lib/
For instance:
-Djava.library.path="/usr/lib/ure/lib"

And then you can change your OfficeManager.

OfficeManager officeManager = new DefaultOfficeManagerConfiguration()
        .setOfficeHome("/usr/lib/openoffice")
        .setConnectionProtocol(OfficeConnectionProtocol.PIPE)
        .setPipeNames("office1","office2") //two pipes
        .setTaskExecutionTimeout(240000L) //4 minutes
        .setTaskQueueTimeout(60000L)  // 1 minute
        .buildOfficeManager().start();



ConverterService3Impl
The following codes performs all the converting. It supports a File or byte[] as input.

This is how you use it:
Lets say you have a PDF file as byte[], and you want to convert this byte to PDF/A as byte.
All you would have to do is call method:



byte[] pdfa = converterService.convertToPDFA(pdfFile);

Similarly, if you have a Document (say a OpenOffice.org writer document) and you want to convert this to PDF you would call the method:

File doc = new File("myDocument.odt");
File pdfDocument = converterService.convert(doc, ".pdf");

Note that you will always get a PDF/A compliant pdf. All you need to do is change the extension from ".pdf" to ".html" and the converter would do the magic.


Here is the source. Please read the comments in the source code if you want to understand it, or just ask in the comment section below.

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ejb.Local;
import javax.ejb.Stateless;

import lombok.Cleanup;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.document.DefaultDocumentFormatRegistry;
import org.artofsolving.jodconverter.document.DocumentFamily;
import org.artofsolving.jodconverter.document.DocumentFormat;
import org.artofsolving.jodconverter.document.DocumentFormatRegistry;

/**
 * This service converts files from one thing to another ie ODT to PDF, DOC to ODT etc
 * @author Shervin Asgari
 *
 */
@Stateless
@Local(ConverterService.class)
public class ConverterService3Impl implements ConverterService {

  private static final String PDF_EXTENSION = ".pdf";
  private static final String PDF = "pdf";  

  // Uncomment these when we want to use them

  // private final int PDFXNONE = 0;
  private final int PDFX1A2001 = 1;
  // private final int PDFX32002 = 2;
  // private final int PDFA1A = 3;
  // private final int PDFA1B = 4; 


  @Logger //Your favourite logger (ie Log4J) could be injected here 
  private Log log;

  public File convert(File inputFile, String extension) throws IOException, ConnectException {
    if (inputFile == null) {
      throw new IOException("The document to be converted is null");
    }

    Pattern p = Pattern.compile("^.?pdf$", Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher(extension);
    OfficeDocumentConverter converter;
    
    //If inputfile is a PDF you will need to use another FormatRegistery, namely DRAWING
    if(FilenameUtils.isExtension(inputFile.getName(), PDF) && m.find()) {
      DocumentFormatRegistry formatRegistry = new DefaultDocumentFormatRegistry();
      formatRegistry.getFormatByExtension(PDF).setInputFamily(DocumentFamily.DRAWING);
      converter = new OfficeDocumentConverter(officeManager, formatRegistry);
    } else {
      converter = new OfficeDocumentConverter(officeManager);
    }
    
    String inputExtension = FilenameUtils.getExtension(inputFile.getName());
    File outputFile = File.createTempFile(FilenameUtils.getBaseName(inputFile.getName()), extension);

    try {
      long startTime = System.currentTimeMillis();
      //If both input and output file is PDF
      if (FilenameUtils.isExtension(inputFile.getName(), PDF) && m.matches()) {
        //We need to add the DocumentFormat with DRAW
        converter.convert(inputFile, outputFile, toFormatPDFA_DRAW());
      } else if(FilenameUtils.isExtension(outputFile.getName(), PDF)) {
        converter.convert(inputFile, outputFile, toFormatPDFA());
      } else {
        converter.convert(inputFile, outputFile);
      }
      long conversionTime = System.currentTimeMillis() - startTime;
      log.info(String.format("successful conversion: %s [%db] to %s in %dms", inputExtension, inputFile.length(), extension, conversionTime));

      return outputFile;
    } catch (Exception exception) {
      log.error(String.format("failed conversion: %s [%db] to %s; %s; input file: %s", inputExtension, inputFile.length(), extension, exception, inputFile.getName()));
      exception.printStackTrace();
      throw new IOException("Converting failed");
    } finally {
      //outputFile.deleteOnExit();
      //inputFile.deleteOnExit();
    }
  }
  
  /**
   * Convert pdf file to pdf/a
   * You will need to install OpenOffice extension (pdf viewer) to get it working
   * @param pdf
   * @return Byte array
   * @throws IOException
   */
  public byte[] convertToPDFA(byte[] pdfByte) throws IOException, ConnectException {
    @Cleanup InputStream is = new ByteArrayInputStream(pdfByte);
    File pdf = createFile(is, PDF_EXTENSION);
    log.debug("PDF is: #0 #1", pdf.getName(), pdf.isFile());
    return convert(pdf);
  }

  
  private byte[] convert(File pdf) throws IOException {
    if (pdf == null) {
      throw new IOException("The document to be converted is null");
    }

    File convertedPdfA = convert(pdf, PDF_EXTENSION);
    @Cleanup final InputStream inputStream = new BufferedInputStream(new FileInputStream(convertedPdfA));
    byte[] pdfa = IOUtils.toByteArray(inputStream);
    return pdfa;
  }

  /**
   * Creates a temp file and writes the content of InputStream to it. doesn't close input
   * 
   * @return File
   */
  private java.io.File createFile(InputStream in, String extension) throws IOException {
    java.io.File f = File.createTempFile("tmpFile", extension);
    @Cleanup BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(f));
    IOUtils.copy(in, out);
    return f;
  }
  
  /**
   * This DocumentFormat must be used when converting from document (not pdf) to pdf/a
   * For some reason "PDF/A-1" is called "SelectPdfVersion" internally; maybe they plan to add other PdfVersions later.
   */
  private DocumentFormat toFormatPDFA() {
    DocumentFormat format = new DocumentFormat("PDF/A", PDF, "application/pdf");
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put("FilterName", "writer_pdf_Export");

    Map<String, Object> filterData = new HashMap<String, Object>();
    filterData.put("SelectPdfVersion", this.PDFX1A2001);
    properties.put("FilterData", filterData);

    format.setStoreProperties(DocumentFamily.TEXT, properties);

    return format;
  }
  
  /**
   * This DocumentFormat must be used when converting from pdf to pdf/a
   * For some reason "PDF/A-1" is called "SelectPdfVersion" internally; maybe they plan to add other PdfVersions later.
   */
  private DocumentFormat toFormatPDFA_DRAW() {
    DocumentFormat format = new DocumentFormat("PDF/A", PDF, "application/pdf");
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put("FilterName", "draw_pdf_Export");

    Map<String, Object> filterData = new HashMap<String, Object>();
    filterData.put("SelectPdfVersion", this.PDFX1A2001);
    properties.put("FilterData", filterData);

    format.setStoreProperties(DocumentFamily.DRAWING, properties);

    return format;
  }

}


Remember to close the connection when your application is quit/shutdown

Labels