Crystal Report Viewer

Friday, June 27. 2008

Today I figured out how to preview Crystal reports that are populated with a list of POJO objects. Here is the code to do it.

The ReportViewerBean can be found in the Eclipse plugins directory, in the ReportViewer.jar library. Make sure to have the .rpt file available on the classpath, as well as your CRConfig.xml file.

javax.swing.SwingUtilities.invokeLater(new Runnable() {

public void run() {
try {
// create a viewer
ReportViewerBean viewer = new ReportViewerBean();
viewer.init(new String[0], null, null, null);

// create a window
JFrame frame = new JFrame("Crystal Report Viewer");
frame.setTitle("Crystal Report Viewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(viewer, BorderLayout.CENTER);
frame.setSize(700, 500);
frame.setVisible(true);

// display the report
IReportSource reportSource = report.getReportSource();
viewer.setReportSource(reportSource);
viewer.start();
} catch (Exception exception) {
System.out.println(exception.toString());
}
}
});

A couple Hibernate hints

Tuesday, May 6. 2008

Here are a couple things I just learned about Hibernate.

1. HibernateProxy.getHibernateLazyInitializer.getImplementation

Cast your objects as this proxy to find the expected class of your objects at runtime. I know it is not elegant but I have found myself in need of this information more than once.

You have probably seen the proxy classes; they look something like this:

    my.Person$$EnhancerByCGLIB$$114dd646

when what you really want is simply this:

    my.Person

2. @BatchSize(size = 100) instead of @Fetch(FetchMode.SUBSELECT)

The problem with FetchMode.SUBSELECT is that Hibernate blindly retrieves all the rows in the collection table, regardless of the size of the page set. BatchSize does not retrieve everything all at once, rather it optimizes the number of queries run when OneToMany collections are loaded for lists of objects. Note that the use-case case for this is one in which the collection of child objects must be loaded right away.

This also prevents the infamous n+1 dilemma encountered when no fetch strategy is specified.

For example, lets say you have Persons (parent) who own Cats (children) and you must list 10 Persons on the page along with the number of Cats owned by each Person. (person.cats.size) Of course you could just bite the bullet and let Hibernate run the 11 queries, that is not a problem. But what if you must list 500 Persons on the page? You don't want to run 500 queries. And you don't want to retrieve all the Cats in the Cat table, which is what happens with the FetchMode.SUBSELECT annotation.

By specifying the batch size, you are telling Hibernate to retrieve the Cats in batches, saving on queries. If you retrieve 10 Persons, and the batch size is set to 100, you will only run 2 queries; 1 to get the Persons and 1 to get the Cats. If you retrieve 200 Persons, and the batch size is 100, you will run 3 queries; 1 to get the Persons and 2 to get the Cats collections. Its a win-win.

Hibernate and HSQLDB

Monday, May 5. 2008

Here is an example of how to pass properties to HSQLDB without having an explicit properties file. These properties tell HSQLDB to shutdown after the last connection is closed, to only store records being accessed in memory (store the rest on disk), and to store no more than 12Mb worth of records in memory.

<!-- JDBC connection properties -->
<property name="hibernate.connection.driver_class">
    org.hsqldb.jdbcDriver
</property>
<property name="hibernate.connection.url">
    jdbc:hsqldb:file:hsqldb;
    shutdown=true;
    hsqldb.default_table_type=cached;
    hsqldb.cache_scale=12;
    hsqldb.cache_size_scale=10
</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>
<property name="hibernate.show_sql">false</property>


No other startup or shutdown configuration is necessary.

Commons DBCP Bug

Monday, March 3. 2008

I have discovered the hard way that the latest released version of DBCP, 1.2.2, is buggy with Mysql 5.x. Anytime the server terminates the connection, (idle timeout, server restart, etc) DBCP will throw a JDBC error rather than establish and return a new java.sql.Connection. Subsequent requests are successful, so the connection must be getting removed from the pool during the error handling procedure.

Version 1.2 (which is not available via the download site) works fine.

I have not tested version 1.2.1. Also I have not conclusively tested version 1.2.2 against Oracle or Postgresql.

Version 1.2.2 was released in April 2007. I could not find a direct bug on the DBCP JIRA project page.

Crystal Reports Java SDK broken image handler

Friday, October 26. 2007

The Crystal Reports Java SDK ships with a broken image handler named crystalimagehandler.jsp. After the image is written and the output stream is closed, an attempt is made to print a final new line character, which fails with an IllegalStateException, because of course the output stream is closed.

The workaround is to use a servlet. But... Crystal Reports has an internal hard-coded reference to crystalimagehandler.jsp, so it must stay. Therefore, inside the jsp image handler, redirect all requests made to crystalimagehandler.jsp to a servlet, as follows:

response.sendRedirect("/crystalImage?" +
    request.getQueryString());


The servlet mapped to /crystalImage contains the code that was in the jsp image handler:

new CrystalImageHandler().handleImage(
    request,
    response,
    getServletConfig().getServletContext());

SimpleDateFormat is not thread-safe

Monday, October 22. 2007

I just learned that SimpleDateFormat is not thread-safe.

"The cause of the problem is that a value (the date portion of calendar) used only for the duration of a single public method (parse) is stored as a member variable."

Java Exception Handling

Monday, February 5. 2007

Here is a good article on how to handle Exceptions in Java:

http://today.java.net/lpt/a/280

Here is a summary of the anti-patterns (what not to do):


  • Throwing Exception
  • Throwing the Kitchen Sink
  • Catching Exception
  • Destructive Wrapping
  • Log and Return Null
  • Catch and Ignore
  • Throw from Within Finally

Also here is what Sun says about unchecked exceptions:

http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

Hack, hack, fix, hope

Thursday, January 11. 2007

When is is ever going to end?

Trang and XMLBeans

Thursday, January 4. 2007

Trang "converts between different schema languages for XML". I use it to change DTD files into XSD files.

Here is how to do it from Ant:

<!-- convert DTD to XSD -->
<java jar="trang.jar"
    fork="true"
    failonerror="true">
    <arg line="-i xmlns=http://example.com/schema/MyExample"/>
    <arg value="${basedir}/dtds/MyExample.dtd"/>
    <arg value="${dist}/schema/MyExample.xsd"/>
</java>


After the XSD is generated, bind it to Java types using XMLBeans, from Apache.

Again, from Ant:

<!-- build JAR of XML beans from XSD -->
<taskdef name="xmlbean"
    classname="org.apache.xmlbeans.impl.tool.XMLBean"
    classpath="path/to/xbean.jar" />

<delete dir="${dist}/schema/classes" />

<xmlbean schema="${dist}/schema/MyExample.xsd"
    destfile="${basedir}/lib/schema/MyExample.jar"
    classgendir="${dist}/schema/classes"
    classpathref="lib.xmlbeans" />


After the schema is compiled, the JavaBeans are ready to use:

XmlOptions options = new XmlOptions();

Map namespaces = new HashMap();
namespaces.put("", "http://example.com/schema/MyExample");
options.setLoadSubstituteNamespaces(namespaces);

MyExampleDocument doc = MyExampleDocument.Factory.parse(
    myExampleXmlFile, options);


Note: The namespace option prevents the parse() method from throwing an error if the XML file has no default namespace defined.

Ant Tip #347: Don't filter your jars

Friday, December 1. 2006

If you run your binary libraries through an Ant filter, they will become corrupted. You will get a ClassNotFoundException when you reference any class within the jar. You will think it is a classpath issue, and will beat yourself over the head for 2 hours trying to figure it out.

This is how it should NOT be done:

<filter filtersfile="${target}.properties" />

<copy todir="${dist}" filtering="true">
    <fileset dir="${basedir}" />
</copy>


This is how it should be done:

<filter filtersfile="${target}.properties" />

<copy todir="${dist}" filtering="true">
    <fileset dir="${basedir}">
        <exclude name="**/*.jar"/>
    </fileset>
</copy>

<copy todir="${dist}">
    <fileset dir="${basedir}/scripts">
        <include name="**/*.jar"/>
    </fileset>
</copy>


Better yet, you could explicitly denote each file to which the filter should be applied. This leaves no room for question.