This blog is mainly about Java...

Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

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, 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());
}

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!

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, 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!

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.

Wednesday, February 4, 2009

How to create and use a WebService with Axis 2 and Seam 2.x in JBoss 4.x

In this example, I will show how you can create a Webservice using Axis 2.
First of all, download the latest version of Axix 2 from http://ws.apache.org/axis2/

To create a WebService in Java EE 5 you can use the annotation @WebService.
We also annotate this class as a seam component so that we can incorporate it in our existing business logic.

This is our WebService:

package somepackage.webservice;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.persistence.EntityManager;

import org.jboss.seam.Component;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.security.Credentials;
import org.jboss.seam.security.Identity;

@Name("fooService")
@Stateless
@WebService(name = "FooService", serviceName = "FooService")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class FooService implements FooServiceLocal {

@In EntityManager entityManager;

@In Credentials credentials;

private boolean login(String username, String password) {
credentials.setUsername(username);
credentials.setPassword(password);
Identity.instance().login();
return Identity.instance().isLoggedIn();
}

private boolean logout() {
Identity.instance().logout();
return !Identity.instance().isLoggedIn();
}

@WebMethod
public List<FooCanonical> getFoo(@WebParam(name = "username")
String username, @WebParam(name = "password")
String password, @WebParam(name = "orgnumber")
String orgnumber) {
// orgnumber can be null!
if (username == null || password == null) {
return null;
}
//First thing we do is to login to ensure that the user has the correct username/password
//We are using basic seam login method
boolean isLoggedIn = login(username, password);

if (isLoggedIn) {

List<FooCanonical> returnList = new ArrayList<FooCanonical>();
//Do some stuff with the list
//Remember to log out
logout();
return returnList;
} else {
// Probably wrong username password
return null;
}
}

}


Next what we need to do, is create a way for this webservice to interact with JBoss through our SOAP definition. We do that by creating a xml file called
standard-jaxws-endpoint-config.xml

<jaxws-config xmlns="urn:jboss:jaxws-config:2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jaxws-config:2.0 jaxws-config_2.1.xsd">
<endpoint-config>
<config-name>Seam WebService Endpoint</config-name>
<pre-handler-chains>
<javaee:handler-chain>
<javaee:protocol-bindings>##SOAP11_HTTP</javaee:protocol-bindings>
<javaee:handler>
<javaee:handler-name>SOAP Request Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.seam.webservice.SOAPRequestHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</pre-handler-chains>
</endpoint-config>
</jaxws-config>

And place this file in the $JBOSS_HOME/resources/META-INF directory.
Now you are done! Deploy your application and look in
http://localhost:8080/jbossws/services 
and see if your WebService is correctly deployed and the wsdl available.
This should look something like this:

Endpoint Namejboss.ws:context=foo-foo,endpoint=FooService
Endpoint Addresshttp://localhost:8080/foo-foo/FooService?wsdl

Next, we will use the Axis2 framework to create client stubs by using axis2-1.4.1 and the script wsdl2java. Navigate to $AXIS_HOME/bin and type in the following command:
./wsdl2java.sh -uri http://127.0.0.1:8080/foo_foo/FooService?wsdl -o build/client

This command will create an ant script under the directly build/client.
Now go to build/client and type ant after setting $AXIS_HOME. This will generate FooService-test-client.jar which we now can use to retrieve data from the WebService in the client. I recommend changing the name to something more appropriate.

In your client, you can call the getFoo WebMethod like this:

FooServiceStub stub;
GetFoo getFoo;

stub = new FooServiceStub();
getFoo = new FooServiceStub.GetFoo();
getFoo.setUsername("username");
getFoo.setPassword("password");
getFoo.setOrgnumber("1234");

FooServiceStub.GetFooE fooImpl = new FooServiceStub.GetFooE();
fooImpl.setGetFoo(getFoo);

//Retrieve the List as an array
FooCanonical[] get_return = stub.getFoo(fooImpl).getGetFooResponse().get_return();
//Do what you want with the array

Note that even if you return a List from the WebService, you will get it as an array. But it is quite easy to put it in a List in the client afterwards. Also remember that the username and password is sendt in clear text, so you might want to send it through https, so it is encrypted.

Wednesday, December 3, 2008

Dynamically generate ODT and PDF documents from Java

I would like to generate a OpenOffice document and a PDF document without having a running OpenOffice service in Java. This was not as easy as it sounds, however I have found a solution.

You can create ODT document fairly easy without having a running instance of OpenOffice. However I have not found an easy way to convert that document to a PDF. However, I found a solution for the latter when running Linux. 

All the following libraries are Open Source .

The easiest way to generate ODT documents from templates is by using a (unmaintaned) library by the name of JOOReport. JOOReport uses Freemarker to create ODT documents based on templates. 

Basically what you need to do is create a template odt document in OpenOffice and whereever you want to insert something, you can insert it with the syntax 
${anythingGoesHere}
ie: My name is ${name}

When you have finished implementing the template, you must then create a properties file defining the values. 

ie. 
name=
age=
birthday=
address=


We can then from the Java program get the Properties file, and fill inn already pre defined variables. We then give the template and the properties file as well as the output odt file in arguments to the program. After the creation is successfull, the easiest way is to call a linux program called 
odt2pdf someFile.odt

which takes the odt file as argument and creates a pdf file with the same name. 

Vouila. As easy as that. You can also style the template as you like and the styling will also be implemented in the generated output file.

This is a standalone program that creates a document from a template and a data file and converts it to the specific format. 


// JOOReports - The Open Source Java/OpenOffice Report Engine 
// Copyright (C) 2004-2006 - Mirko Nasato 
// 
// This library 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 2.1 of the License, or (at your option) any later version. 
// 
// This library 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. 
// http://www.gnu.org/copyleft/lesser.html 
// 
package net.sf.jooreports.tools; 

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.net.ConnectException; 
import java.util.Properties; 

import org.apache.commons.io.FilenameUtils; 

import net.sf.jooreports.converter.DocumentConverter; 
import net.sf.jooreports.openoffice.connection.OpenOfficeConnection; 
import net.sf.jooreports.openoffice.connection.SocketOpenOfficeConnection; 
import net.sf.jooreports.openoffice.converter.OpenOfficeDocumentConverter; 
import net.sf.jooreports.templates.DocumentTemplate; 
import net.sf.jooreports.templates.UnzippedDocumentTemplate; 
import net.sf.jooreports.templates.ZippedDocumentTemplate; 
import freemarker.ext.dom.NodeModel; 

/** 
 * Command line tool to create a document from a template and a data file 
 * and convert it to the specified format. 
 * 


 * The data file can be in XML format or a simple .properties file. 
 * 


 * Requires an OpenOffice.org service to be running on localhost:8100 
 * (if the output format is other than ODT). 
 */ 
public class CreateAndConvertDocument { 

  public static void main(String[] args) throws Exception { 
  if (args.length < 3) { System.err.println("USAGE: "+ CreateAndConvertDocument.class.getName() +" "); 
  System.exit(0); 
  }  
  File templateFile = new File(args[0]); 
  File dataFile = new File(args[1]); 
  File outputFile = new File(args[2]); 

  DocumentTemplate template = null; 
  if (templateFile.isDirectory()) { 
  template = new UnzippedDocumentTemplate(templateFile); 
  } else { 
  template = new ZippedDocumentTemplate(templateFile); 
  } 
   
  Object model = null; 
  String dataFileExtension = FilenameUtils.getExtension(dataFile.getName()); 
  if (dataFileExtension.equals("xml")) { 
  model = NodeModel.parse(dataFile); 
  } else if (dataFileExtension.equals("properties")) { 
  Properties properties = new Properties(); 
  properties.load(new FileInputStream(dataFile)); 
  model = properties; 
  } else { 
  throw new IllegalArgumentException("data file must be 'xml' or 'properties'; unsupported type: " + dataFileExtension); 
  } 
   
  if ("odt".equals(FilenameUtils.getExtension(outputFile.getName()))) { 
  template.createDocument(model, new FileOutputStream(outputFile)); 
  } else { 
  OpenOfficeConnection connection = new SocketOpenOfficeConnection(); 
  try { 
  connection.connect(); 
  } catch (ConnectException connectException) { 
  System.err.println("ERROR: connection failed. Please make sure OpenOffice.org is running and listening on port "+ SocketOpenOfficeConnection.DEFAULT_PORT +"."); 
  System.exit(1); 
  } 
   
  File temporaryFile = File.createTempFile("document", ".odt"); 
  temporaryFile.deleteOnExit(); 
  template.createDocument(model, new FileOutputStream(temporaryFile)); 
  
  try { 
  DocumentConverter converter = new OpenOfficeDocumentConverter(connection); 
  converter.convert(temporaryFile, outputFile); 
  } finally { 
  connection.disconnect(); 
  } 
  } 
  } 
}




Friday, August 22, 2008

Using Encryption (Jasypt) in Seam 2.0 and how to search on encrypted values

In our project we had to encrypt all fields in the person table that can identify a person.
I found a nice framework that makes encryption quite easy called Jasypt and more specifically, it had very nice and easy configuration for Seam 2, which can be found here: Jasypt with Seam 2.

The problem however is that all our searches that we had created for the person fields that are now encrypted fail. For obvious reasons, you cannot compare (run LIKE) on encrypted fields.
But the user demanded to still be able to filter the search based on the encrypted values.
There are two theories on how I could do this.

The first, was to encrypt the user input and then try to find a match against the encrypted fields. However since Jasypt uses SALT I cannot easily do this because SALT generates x amount of random bytes and makes two equal values different chipertext. So to solve that I would have to remove the SALT and do the comparing. However we have very little time to solve this, so I went with the second option which I really wanted to avoid.

What I do know is create a query based on the values that are not encrypted and return the List. However, it is not certain that the user will enter one of the values that is not encrypted, so the search will then retrieve all Person objects, and then I loop through the List, decrypt the values (which Jasypt automatically does) and compare against what the user has inputted in the search criteria and then return the correct list.
This is a very cumbersome method to retrieve the List. Having all the Person objects in memory is not feasible. I will have to add some sort of caching so that it at least will be better when it is in production, but still this is something I would like to avoid. (Yes I know I have all the objects decrypted in the memory/cache, but I don't see any other solution)

If anyone has other ideas on how could be solved, then please leave a comment or contact me.

Wednesday, May 7, 2008

Seam-gen problems when having MyISAM as engine in MySQL

I tried running seam-gen on an existing MySQL database, where the engine is MyISAM.
Jboss-seam-2.0.1.GA is the version of seam I was running.

The strangest thing happened. When I reversed engineered the database to create entity beans, the foreign keys where not Objects like it is normally, but rather String.
For instance, in our Address entity bean, I have a foreign key that is mapped to the Country entity bean.
Normally the Address.java should look something like this:

@Entity
@Table(name = "address")
public class Address implements java.io.Serializable {

private Integer addressId;
//The ID
private Country country; //The foreign key
....
}


However, the foreign key was generated like this:
private String countryId; //The foreign key

But when I changed the MySQL engine to use InnoDB instead, and I ran a new seam-gen, it was generated correctly.

Just a heads up if someone encounters the same problem. I would guess it is a bug in either seam or hibernate. Probably hibernate, since it is hibernate that does the generation.

Monday, April 28, 2008

No JavaOne for me

I was planning to go to JavaOne in San Francisco this year, but my manager gave me the thumbs down the last minute. We have recently won a contract from the Kommuneforlaget, and we are going to work on an electronic application for alcohol serving. They need me on the team ASAP so I can't go :-(

I had ordered the ticket and everything. Anyways, I'm glad to be on the team, it looks like a really cool project where we are going to use JBoss, RHEL, JBoss JBPM, Seam, Mule or JBoss ESB and other cool stuff.

So 1. may is the date I will start working with the Linpro team...

Labels