Sunday, June 19, 2011

Raw data and format in Jasper Reports

Raw data let us say a Float or a BigDecimal can be formatted in Excel following some patterns in a way that it is clearer for printing. However the raw data remains intact in a way that formulas can be applied later on those raw numbers. In accounting and financing this is a simple yet important concept to understand: separation between data and format.

JasperReports JRXML format uses its own formatting which is different from Excel and so when you want to export to Excel directly from iReport for example two things are going to happen:
  1. Probably you will have no formatting rule available and you will end up with an approximation of the real formatting you are after
  2. Your raw data could disappear and you get in the Excel output just exacty what the formatting in JRXML is specifying

When my attention was brought to this problem by a member of my current team I saw a post with some solutions I did not like.

The reason why I did not like the solutions (besides the fact they are not addressing the two points I described above) is that they basically violate separation of concerns.

The solution for this problem is indeed addressing the problem from the right angle.

It allows to format anything for non excel output from iReport (jrxml). It then relies on mapping to translate the custom jrxml format to the proprietary to Excel syntax when that format is needed.

I only came to that post after some research and the more I research the more convince I am people get easily lost nowadays when searching for solutions on the web. This is of course a result of a non semantic web markup and consequently a poor search engine filtering. Still there is something that can be done if you stick to concepts and you refine your terms following them.

Here is my personal story when I researched this issue. This could probably help others. Remember do never get the first response you get from Google as the final response to your problem:

  1. I found a similar issue within the JasperReports forums, not from Google. Then I asked if a solution was found

  2. I realized using JRXlsAbstractExporter was apparently the way to go.

  3. And so I finally got it.
  4. As this is a solution addressable only from the JR Excel API Exporter, if you are really stuck with POI Exporter then what to do? I could not find anything but as I knew from simple concept this was a task to be done by the exporter and the exporter uses the POI API then I searched for the necessary formatting and I got into this post

I feel like posting how you research is as useful as posting a solution. A good researcher cannot give up. A path must be follow till the end which is a Proof Of Concept (POC). There is no other way.

If you are the architect you must do that job. If you are the CTO be sure you do it yourself or have an architect to delegate to. If you are a developer and you do this you are on your way to become a good architect and a good hands on CTO.

Tuesday, June 14, 2011

Test Driven Bug Fixing for HTML Jasper Reports

Test Driven Bug Fixing (TDBF) is a must do as I have discussed before.

The structure of the site (HTML) can be effectively tested as we can assert the elements position (layout). The content can be asserted as we inspect the DOM. And that is just the beginning: Behaviour (javascript) and look and feel (CSS) can be tested as well.

Your reports might have a component of behavior and look and feel but the most important part to test is structure and content. Jasper Reports has several exporters and one of them is HTML. The HTML is absolute positioned and that creates the perfect scenario for testing.

If a bug has to be addressed then be sure you provide an automated test to ensure it will never come back.

All you need to do is:
  1. Business Analyst provides a test scenario where assertion on a cell content are made: The number for the current balance for this period for this account must be $1,000.00.
  2. Developer runs the Jasper Report from Firefox (Firebug plugin installed and if you want to get serious about it XPath Checker plugin installed as well) using HTML output.
  3. Developer runs the Jasper Report from Firefox using HTML output. He right click on the element and select "Inspect Element":
    <span style="position:absolute;left:388px;top:226px;width:72px;height:10px;text-align: right;">
    <span style="font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif; color: #000000; font-size: 8px;">100,000</span>
    </span>
    
  4. From Firebug you see right away a way to test both position and content. Here it is with XPATH:
    //span[@style="position:absolute;left:388px;top:226px;width:72px;height:10px;text-align: right;"]/span
    
  5. There is an advantage and a problem with the above.
    Advantage: It allows to assert not only the value but the position of the element
    Disadvantage: If the element absolute position is changed the test case will need to be changed. If we are interested in just asserting the data and not the absolute position of the element then the xpath is better retrieved from Firebug: Right click on the element on the firebug left pane and select "Copy XPath" to get something like:
    /html/body/table/tbody/tr/td[2]/div/span[71]/span
    
  6. Finally if you want to pleay with XPath a little bit more and assert really complicated scenarios start by right clicking something in the page content and selecting "View XPath". A new screen will come up from where you can see the xpath to get that element (be aware of the schema which you can remove for cleaner xpath expression). Go ahead and evaluate any random XPath from there as well.

Thursday, June 09, 2011

IReport: Dynamically hide / show columns for Jasper Reports

I could not understand why Google was not my friend this time. And as far as I can tell there is no a single post with a tutorial on how to hide columns in a Jasper Report from iReport.

It is difficult for me to accept there is no solution to respect separation of concerns in software architecture and development.

It is my eternal fight. Developers are tempted to embed look and feel code (View) inside business logic or routing code (Controller). In some cases even in the database (Model).

When I designed the Real Time Report Architecture based on Jasper reports I always thought about someone working with iReport building the appearance of the report while someone would just make sure the database would be available in a way that simple queries could be made to create report datasets. Finally from datasets it should be a piece of cake to render a final product using graphical components.

Suddenly a road block from the team: iReport does not allow to hide columns on demand. Let us use Dynamic Jasper. In fact why are we going with this whole design in iReport, we could just do everything in Jasper, yeah from Java!

My answer: Nope, that violates separation of concerns. I will try my best to get a clean solution and if I ever use Dynamic Jasper it would be to provide a temporary hack while a bug or feature request that I fill in Tracker is resolved. And in any case I would always read the JRXML as template and modify that from code. I will never give up the concept, that is as important as your environment. The architect knows it very well.

So I first tried with "printWhenExpression" node that can be used like:
<textField>
    <reportElement x="0" y="0" width="100" height="10" isPrintWhenDetailOverflows="true">
     <printWhenExpression><![CDATA[new Boolean($P{showAccount})]]></printWhenExpression>
    </reportElement>
    <textElement>
     <font size="6"/>
    </textElement>
    <textFieldExpression class="java.lang.String"><![CDATA[$F{account}]]></textFieldExpression>
   </textField>

There are two problems with this solution:
  1. It is verbose because you need to declare it not only for the value but also for any other element in the same vertical position for example lines and column names.
  2. An empty vertical space will remain in the column position in other words the rest of the columns will never move from their original position.


A simple concept called absolute position is the one making our lives miserable here.

The answer to this problem is then to find a different component that allows for relative positioning. For sure the author of this still to be found component allows to remove the column and make the rest move to the empty position if of course the replaced column is on the left. And that component was unfortunately missing in JasperReports for sometime up until a tracker ticket was resolved. So the good news is that for a whole year this component has been available.

The component we need is a simple table or "Table Component" which is the way JasperReports call it. Now, anybody would think that something that has been part of JasperReports for a year should already have a tutorial especially when it solves such an important problem: Separation of concerns. The web is plenty of examples to use native library, Dynamic Jasper, JasperReports server, velocity templates. It came to my attention that actually nobody thought about using XSLT which would be in my opinion the correct way if you want to apply a transformation to an XML document like jrxml. Hey, BTW there is also XGAWK which is faster than XSLT: Oops no! Forget it, is not integrated with Java. The bottom line is there is not such tutorial and that is why I created this one.

I spent so much time on this one to help my team that I said: I will take an extra hour and document what I did with a simple example not only for my team but to give back to the community. After all Jose Marti said: Every man should have the right to be educated and in return with his effort contribute to the education of others.

Here is how to hide columns with an example:
  1. The first thing you will need is a database. I use sqlite3 and here is the schema for the balance.db:
    'balance.sql
    DROP TABLE IF EXISTS `balance`;
    CREATE TABLE `balance` (
      `category` varchar,
      `account` varchar,
      `opening_balance` float,
      `ending_balance` float,
      `debit` float,
      `credit` float
    );
    
    INSERT INTO `balance` values ('Food', 'City Bank', 1000.00, 800.00, 200.00, 0.00);
    INSERT INTO `balance` values ('Food', 'J.P Morgan', 500.00, 400.00, 100.00, 0.00);
    INSERT INTO `balance` values ('Gas', 'J.P Morgan', 400.00, 200.00, 200.00, 0.00);
    INSERT INTO `balance` values ('Clothes', 'City Bank', 800.00, 700.00, 100.00, 0.00);
    INSERT INTO `balance` values ('Payrol', 'City Bank', 700.00, 1700.00, 0.00, 1000.00);
    INSERT INTO `balance` values ('Business', 'J.P Morgan', 700.00, 1700.00, 0.00, 1000.00);
    
  2. Create the db:
    sqlite3 balance.db < balance.sql
    
  3. From iReport create a new Datasource pointing it to your database. Here is the config for my local sqlite datase:
    Name: balance
    JDBC Driver: org.sqlite.JDBC
    JDBC URL: jdbc:sqlite:/Users/nestor/Downloads/balance.db
    
    Note that you must have the sqlite3 JDBC driver. As I explained in a previous post just go to "preferences | classpath" and point to sqlite3 driver file: sqlitejdbc-v056.jar. Of course you need to download that file if you do not have it. Just Google it.
  4. Create a local file named balance.jrxml with the below content. Open it from iReport:
    <?xml version="1.0" encoding="UTF-8"?>
    <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="balance" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="30" bottomMargin="30" isFloatColumnFooter="true">
     <property name="ireport.zoom" value="1.0"/>
     <property name="ireport.x" value="0"/>
     <property name="ireport.y" value="0"/>
     <style name="Sans_Normal" isDefault="true" fontName="DejaVu Sans" fontSize="10" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false"/>
     <style name="Table">
      <box>
       <pen lineWidth="1.0" lineColor="#FF0000"/>
      </box>
     </style>
     <style name="TableHeader" mode="Opaque" backcolor="#808080"/>
     <style name="TableFooter" mode="Opaque" backcolor="#C0C0C0"/>
     <subDataset name="TableData">
      <queryString language="SQL">
       <![CDATA[SELECT
                                 category,
                                 account,
                                 opening_balance,
                                 ending_balance,
                                 debit,
                                 credit
                            FROM balance]]>
      </queryString>
      <field name="category" class="java.lang.Object"/>
      <field name="account" class="java.lang.Object"/>
      <field name="opening_balance" class="java.lang.Object"/>
      <field name="ending_balance" class="java.lang.Object"/>
      <field name="debit" class="java.lang.Object"/>
      <field name="credit" class="java.lang.Object"/>
      <variable name="creditSum" class="java.lang.Double" calculation="Sum">
       <variableExpression><![CDATA[$F{credit}]]></variableExpression>
      </variable>
      <variable name="debitSum" class="java.lang.Double" calculation="Sum">
       <variableExpression><![CDATA[$F{debit}]]></variableExpression>
      </variable>
     </subDataset>
     <parameter name="showOpeningBalance" class="java.lang.Boolean">
      <defaultValueExpression><![CDATA[new Boolean(true)]]></defaultValueExpression>
     </parameter>
     <parameter name="showEndingBalance" class="java.lang.Boolean">
      <defaultValueExpression><![CDATA[new Boolean(true)]]></defaultValueExpression>
     </parameter>
     <parameter name="showCategory" class="java.lang.Boolean">
      <defaultValueExpression><![CDATA[new Boolean(true)]]></defaultValueExpression>
     </parameter>
     <parameter name="showAccount" class="java.lang.Boolean">
      <defaultValueExpression><![CDATA[new Boolean(true)]]></defaultValueExpression>
     </parameter>
     <parameter name="showDebit" class="java.lang.Boolean">
      <defaultValueExpression><![CDATA[new Boolean(true)]]></defaultValueExpression>
     </parameter>
     <parameter name="showCredit" class="java.lang.Boolean">
      <defaultValueExpression><![CDATA[new Boolean(true)]]></defaultValueExpression>
     </parameter>
     <title>
      <band height="150">
       <componentElement>
        <reportElement style="Table" x="0" y="50" width="555" height="100"/>
        <c:table xmlns:c="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd">
         <datasetRun subDataset="TableData">
          <connectionExpression><![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression>
         </datasetRun>
         <c:column width="80">
          <printWhenExpression><![CDATA[new Boolean($P{showCategory})]]></printWhenExpression>
          <c:columnHeader style="TableHeader" height="30" rowSpan="2">
           <box leftPadding="10">
            <pen lineColor="#000000"/>
            <bottomPen lineWidth="0.5"/>
           </box>
           <staticText>
            <reportElement x="0" y="0" width="70" height="30"/>
            <textElement verticalAlignment="Middle">
             <font size="12" isBold="true"/>
            </textElement>
            <text><![CDATA[Category]]></text>
           </staticText>
          </c:columnHeader>
          <c:columnFooter style="TableFooter" height="15">
           <box leftPadding="10">
            <pen lineColor="#000000"/>
           </box>
           <staticText>
            <reportElement x="0" y="0" width="70" height="15"/>
            <textElement verticalAlignment="Middle">
             <font size="12" isBold="true"/>
            </textElement>
            <text><![CDATA[Totals]]></text>
           </staticText>
          </c:columnFooter>
          <c:detailCell height="15">
           <box leftPadding="10">
            <bottomPen lineWidth="0.5"/>
           </box>
           <textField isStretchWithOverflow="true">
            <reportElement x="0" y="0" width="70" height="15"/>
            <textElement/>
            <textFieldExpression class="java.lang.String"><![CDATA[$F{category}]]></textFieldExpression>
           </textField>
          </c:detailCell>
         </c:column>
         <c:column width="80">
          <printWhenExpression><![CDATA[new Boolean($P{showAccount})]]></printWhenExpression>
          <c:columnHeader style="TableHeader" height="30" rowSpan="2">
           <box leftPadding="10">
            <pen lineColor="#000000"/>
            <leftPen lineWidth="0.5"/>
            <bottomPen lineWidth="0.5"/>
           </box>
           <staticText>
            <reportElement x="0" y="0" width="70" height="30"/>
            <textElement verticalAlignment="Middle">
             <font size="12" isBold="true"/>
            </textElement>
            <text><![CDATA[Account]]></text>
           </staticText>
          </c:columnHeader>
          <c:columnFooter style="TableFooter" height="15">
           <box leftPadding="10">
            <pen lineColor="#000000"/>
            <leftPen lineWidth="0.5"/>
           </box>
           <staticText>
            <reportElement x="0" y="0" width="70" height="15"/>
            <textElement verticalAlignment="Middle">
             <font size="12" isBold="true"/>
            </textElement>
            <text><![CDATA[-]]></text>
           </staticText>
          </c:columnFooter>
          <c:detailCell height="15">
           <box leftPadding="10">
            <leftPen lineWidth="0.5"/>
            <bottomPen lineWidth="0.5"/>
           </box>
           <textField>
            <reportElement x="0" y="0" width="70" height="15"/>
            <textElement/>
            <textFieldExpression class="java.lang.String"><![CDATA[$F{account}]]></textFieldExpression>
           </textField>
          </c:detailCell>
         </c:column>
         <c:columnGroup width="200">
          <c:columnHeader style="TableHeader" height="15">
           <box>
            <pen lineWidth="0.5" lineColor="#000000"/>
           </box>
           <staticText>
            <reportElement x="0" y="0" width="190" height="15"/>
            <textElement textAlignment="Center">
             <font size="12" isBold="true"/>
            </textElement>
            <text><![CDATA[Balance]]></text>
           </staticText>
          </c:columnHeader>
          <c:column width="100">
           <printWhenExpression><![CDATA[new Boolean($P{showOpeningBalance})]]></printWhenExpression>
           <c:columnHeader style="TableHeader" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <topPen lineWidth="0.5"/>
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <staticText>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement verticalAlignment="Middle">
              <font size="12" isBold="true"/>
             </textElement>
             <text><![CDATA[Opening]]></text>
            </staticText>
           </c:columnHeader>
           <c:columnFooter style="TableFooter" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <leftPen lineWidth="0.5"/>
            </box>
            <staticText>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement verticalAlignment="Middle">
              <font size="12" isBold="true"/>
             </textElement>
             <text><![CDATA[-]]></text>
            </staticText>
           </c:columnFooter>
           <c:detailCell height="15">
            <box leftPadding="10">
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <textField>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement/>
             <textFieldExpression class="java.lang.Double"><![CDATA[$F{opening_balance}]]></textFieldExpression>
            </textField>
           </c:detailCell>
          </c:column>
          <c:column width="100">
           <printWhenExpression><![CDATA[new Boolean($P{showEndingBalance})]]></printWhenExpression>
           <c:columnHeader style="TableHeader" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <topPen lineWidth="0.5"/>
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <staticText>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement>
              <font size="12" isBold="true"/>
             </textElement>
             <text><![CDATA[Ending]]></text>
            </staticText>
           </c:columnHeader>
           <c:columnFooter style="TableFooter" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <leftPen lineWidth="0.5"/>
            </box>
            <staticText>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement>
              <font size="12" isBold="true"/>
             </textElement>
             <text><![CDATA[-]]></text>
            </staticText>
           </c:columnFooter>
           <c:detailCell height="15">
            <box leftPadding="10">
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <textField>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement/>
             <textFieldExpression class="java.lang.Double"><![CDATA[$F{ending_balance}]]></textFieldExpression>
            </textField>
           </c:detailCell>
          </c:column>
         </c:columnGroup>
         <c:columnGroup width="200">
          <c:columnHeader style="TableHeader" height="15">
           <box>
            <pen lineWidth="0.5" lineColor="#000000"/>
           </box>
           <staticText>
            <reportElement x="0" y="0" width="190" height="15"/>
            <textElement textAlignment="Center">
             <font size="12" isBold="true"/>
            </textElement>
            <text><![CDATA[Transaction]]></text>
           </staticText>
          </c:columnHeader>
          <c:column width="100">
           <printWhenExpression><![CDATA[new Boolean($P{showDebit})]]></printWhenExpression>
           <c:columnHeader style="TableHeader" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <topPen lineWidth="0.5"/>
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <staticText>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement verticalAlignment="Middle">
              <font size="12" isBold="true"/>
             </textElement>
             <text><![CDATA[Debit]]></text>
            </staticText>
           </c:columnHeader>
           <c:columnFooter style="TableFooter" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <leftPen lineWidth="0.5"/>
            </box>
            <textField>
             <reportElement x="0" y="0" width="50" height="15"/>
             <textElement>
              <font size="12" isBold="true"/>
             </textElement>
             <textFieldExpression class="java.lang.Double"><![CDATA[$V{debitSum}]]></textFieldExpression>
            </textField>
           </c:columnFooter>
           <c:detailCell height="15">
            <box leftPadding="10">
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <textField>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement/>
             <textFieldExpression class="java.lang.Double"><![CDATA[$F{debit}]]></textFieldExpression>
            </textField>
           </c:detailCell>
          </c:column>
          <c:column width="100">
           <printWhenExpression><![CDATA[new Boolean($P{showCredit})]]></printWhenExpression>
           <c:columnHeader style="TableHeader" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <topPen lineWidth="0.5"/>
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <staticText>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement>
              <font size="12" isBold="true"/>
             </textElement>
             <text><![CDATA[Credit]]></text>
            </staticText>
           </c:columnHeader>
           <c:columnFooter style="TableFooter" height="15">
            <box leftPadding="10">
             <pen lineColor="#000000"/>
             <leftPen lineWidth="0.5"/>
            </box>
            <textField>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement>
              <font size="12" isBold="true"/>
             </textElement>
             <textFieldExpression class="java.lang.Double"><![CDATA[$V{creditSum}]]></textFieldExpression>
            </textField>
           </c:columnFooter>
           <c:detailCell height="15">
            <box leftPadding="10">
             <leftPen lineWidth="0.5"/>
             <bottomPen lineWidth="0.5"/>
            </box>
            <textField>
             <reportElement x="0" y="0" width="90" height="15"/>
             <textElement/>
             <textFieldExpression class="java.lang.Double"><![CDATA[$F{credit}]]></textFieldExpression>
            </textField>
           </c:detailCell>
          </c:column>
         </c:columnGroup>
        </c:table>
       </componentElement>
      </band>
     </title>
    </jasperReport>
    
  5. Click on Preview and it will ask you for true or false to hide or show columns in your report. This uses our old friend printWhenExpression but now it behaves correctly hiding the whole column.
Here are the results with a couple of screenshots. The first one shows all columns while the second shows only the last columns (Balance and Transaction). Note that the original with for the shifting columns (Balance and Transaction) is preserved when the columns are shifted.
This report was built starting from the TableReport in demo/samples/table directory from the JasperReports 4.0.2 distribution, however I faced several issues that I only got resolved when I hacked into the schema files especially in one of them:
$ less /Users/nestor/Downloads/jasperreports-4.0.2//build/classes/net/sf/jasperreports/components/components.xsd
BTW the xsd URL that is used in the demo file is simply unavailable which makes it difficult to work with an IDE because you lose the autocomplete functionality.

Take a look at the totals row. It took me a while to get there just because I was trying to use "staticText" instead of "textField" which has the node "textFieldExpression" in which you can refer to variables created inside a "subDataset" (Where you define also the query and the fields mapping)

Take a look at "datasetRun". I prefer to use connection instead of datasource.

Finally see how easy is to group columns. Just the way it is supposed to be. It makes me think about (I am not going to mention the year here) when I first saw HTML. I was so excited to work with tables (years later I was and I am still fighting them when used for layout but that is a different story).

The rest of the components should be pretty obvious and the whole report by this time self explanatory.

I hope to see more people pushing for separation of concerns in the JasperReports community, after all you wouldn't send HTML code from your Servelt, would you?

Thursday, June 02, 2011

Error getting POM for org.apache.maven.plugins:maven-eclipse-plugin

From time to time I need to get back to this error
Reason: Error getting POM for 'org.apache.maven.plugins:maven-eclipse-plugin' from the repository: Failed to resolve artifact, possibly due to a repository list that is not appropriately equipped for this artifact's metadata.
  org.apache.maven.plugins:maven-eclipse-plugin:pom:2.9-SNAPSHOT

As it happens with other maven dependencies their pom.xml file gets somehow modified and the plugin does not work as expected anymore.

The solution here is to navigate to your local repository, delete the dependency and try to download it again. If it still fails (clearly a pom.xml that has not been corrected) then you need to edit the metadata file. In my case:
/Users/nestor/.m2/repository/org/apache/maven/plugins/maven-eclipse-plugin/maven-metadata-central.xml 

You will notice the "latest node", for example:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-eclipse-plugin</artifactId>
  <versioning>
    <latest>2.9-SNAPSHOT</latest>
    <release>2.8</release>
    <versions>
      <version>2.0-beta-1</version>
      <version>2.0-beta-2</version>
      <version>2.0</version>
      <version>2.1</version>
      <version>2.2</version>
      <version>2.3</version>
      <version>2.4</version>
      <version>2.5</version>
      <version>2.5.1</version>
      <version>2.5.2-SNAPSHOT</version>
      <version>2.6-SNAPSHOT</version>
      <version>2.6</version>
      <version>2.6.1-SNAPSHOT</version>
      <version>2.7-SNAPSHOT</version>
      <version>2.7</version>
      <version>2.8-SNAPSHOT</version>
      <version>2.8</version>
      <version>2.9-SNAPSHOT</version>
    </versions>
    <lastUpdated>20101028062425</lastUpdated>
  </versioning>
</metadata>

Just remove the "latest" node or edit it to reflect the version you want to use (probably a previous stable version) and problem solved

Wednesday, June 01, 2011

JSP JSTL to render a tree menu

Sometimes we simply do not want to use Javascript to render an HTML tree.

Recursion is a common technique used to iterate through the Tree object and render it in HTML using ordered or unordered lists.

From JSP you could use a function but scriptlets are not desired as we know. A better practice is to use jsp:include to reload the same JSP page for the children of the current node.

Here is an example that builds a vertical menu keeping the needed separation of concerns in your architecture.

First there is a POJO encapsulating typical menu fields:
import java.util.ArrayList;
import java.util.List;

public class MenuItem {
    private String key;
    private String url;
    private List<MenuItem> menuItems;
    private boolean selected;
    
    public MenuItem(String key, String url, List<MenuItem> menuItems, boolean selected) {
        super();
        this.key = key;
        this.url = url;
        this.menuItems = menuItems;
        this.selected = selected;
    }
    
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public List<MenuItem> getMenuItems() {
        return menuItems;
    }
    public void setMenuItems(List<MenuItem> menuItems) {
        this.menuItems = menuItems;
    }  
    public void addMenuItem(MenuItem menuItem){
        if(menuItems == null){
            menuItems = new ArrayList<MenuItem>();
        }
        menuItems.add(menuItem);
    }
    public boolean isSelected() {
        return selected;
    }
    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

In your controller you should then fill the verticalMenu list:
List<MenuItem> verticalMenu = new ArrayList<MenuItem>();

The JSP calls itself:
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<ul>
<c:forEach var="menuItem" items="${verticalMenu}">
    <c:set var="verticalMenu" value="${menuItem.menuItems}" scope="request"/>
    <li>
        <c:choose>
            <c:when test="${menuItem.selected}">
                <a href="#" class="selected"><span><c:out value="${menuItem.key}" escapeXml="true"/></span></a>
            </c:when>
            <c:otherwise>
                <c:choose>
                    <c:when test="${ ! empty menuItem.url }">
                        <a href="<spring:url value="${menuItem.url}"/>"><span><c:out value="${menuItem.key}" escapeXml="true"/></span></a>
                    </c:when>
                    <c:otherwise>
                       <span><c:out value="${menuItem.key}" escapeXml="true"/></span>
                    </c:otherwise>
                </c:choose>
            </c:otherwise>
        </c:choose>
        <c:if test="${fn:length(menuItem.menuItems) > 0}">
            <jsp:include page="/WEB-INF/jsp/verticalMenu.jsp"/>
        </c:if>
    </li>
</c:forEach>
</ul>

And the CSS makes some adjustments:
#sidebar ul {
    margin: 0;
    padding: 0;
    list-style-type: none;
}

#sidebar ul ul {
    margin-left: 10px; 
}

#sidebar li {
    display: block;
    margin: 0;
    padding: 1px 0 0 2px; 
}

Sunday, May 29, 2011

set editor for crontab solaris 555

I was trying to use "crontab -e" under Solaris and I was getting back a number "555". This is related to EDITOR variable missing. Here is how it gets solved (Using bash shell)
$ vi ~/.bash_profile
export EDITOR=/usr/bin/vi
$ source ~/.bash_profile
$ crontab -e

Sunday, May 22, 2011

No message found under code '...' for locale

This error can occur even if the code is in the resources file for example when rendering error pages resulting from Tomcat internal errors (500) like JSP errors. We better configure Spring to use the Reloadable Resource Message Source:
<bean id="messageSource"
         class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
         <!--  <property name="basename" value="classpath:messages" />  -->
         <property name="basenames">
             <value>/WEB-INF/i18n/messages</value>
         </property> 
         <property name="cacheSeconds">
             <value>60</value>
         </property>
         <property name="fallbackToSystemLocale" value="false" />
     </bean>

JSP Spring Taglib for your error pages must contain a default value (not very i18n friendly) otherwise when the error pops up the JSP will fail with "No message found under code 'password' for locale 'en_US'"
<spring:message code="password" text="Password"/>

A special case for this issue is when Spring tries to parse an empty code. To avoid the issue in that case just define an empty code line in message.properties. Something like "=" or "=Empty" or "=Null" should do the trick.

Followers