Wednesday, June 29, 2011

The palest ink is better than the best memory

To document or not to document, that's the question.

When in doubt, write it out

Documentation saves hours of research and frustration and that translates into money. Yet so many IT managers tolerate systems without documentation while others go crazy after formalizations that end up in unmaintained material.

I still have to see documentation that is kept current. I am doing my best with my current team trying to enforce that practice. My opinion is documenting what you do is just about showing respect to your partners.

I am usually asked for the best anti-spyware or anti-virus for Windows and I always say: Rebuild your system every six months. Just have it documented: Basically keep a backup of your applications and related metadata and clear instructions as to how to recreate your system from scratch. Some years ago when I was using Windows as my main OS I found myself rebuilding my Windows box so often that I developed a procedure (part of it scripted I have to admit) that allowed me in just 2 hours to recreate my whole environment. That included: Apache, PHP, Java, Tomcat, JBoss, Eclipse, MS Office just to name few of them.

Nowadays I use VirtualBox and recovering Windows is just a matter of going to a safe Snapshot so the only reason for me to rebuild from scratch would be a company change (because of licensing)

The same happens in the world of OS virtualization. If you find that your P2V is difficult, go for a rebuild. With good documentation this could take anytime from 2 to 8 hours. Way less of what you will spend troubleshooting incompatibilities with your tools, different source and destination hardware etc. And if you do not have documentation you better start it.

This is exactly what happened to me when I tried to convert my Ubuntu 10 Server box into a VMware Server VM.

VMware converter will not work for Ubuntu 10. It is simply not supported http://www.vmware.com/support/converter/doc/releasenotes_conv40.html#platf

I tried anyway to install it (you know we always try just in case :-) but as you see below it is totally incompatible:

[several lines like below]
...
The script you are attempting to invoke has been converted to an Upstart
job, but lsb-header is not supported for Upstart jobs.
insserv: warning: script 'atd' missing LSB tags and overrides
insserv: Default-Start undefined, assuming empty start runlevel(s) for script `atd'
insserv: Default-Stop  undefined, assuming empty stop  runlevel(s) for script `atd'
The script you are attempting to invoke has been converted to an Upstart
job, but lsb-header is not supported for Upstart jobs.
insserv: warning: script 'udevmonitor' missing LSB tags and overrides
insserv: Default-Start undefined, assuming empty start runlevel(s) for script `udevmonitor'
insserv: Default-Stop  undefined, assuming empty stop  runlevel(s) for script `udevmonitor'
...
[more lines like above]

While I could have spent more time trying some other converters I decided to spend 8 hours building the box from scratch. But of course I had good old school documentation for building that box. WIKI is your fiend, don't forget that team member!

As the Chinese proverb says "The palest ink is better than the best memory"

Wednesday, June 22, 2011

Installing Configuring and Using Monit in Ubuntu

This is so straight forward and well documented in the Monit project that I wouldn't post about this if not just to allow those users that are starting with Linux and monitoring is their next concern.

The steps here should allow the user to install monit and monitor Google home page in no more than 5 minutes. It is always gratifying to get things running while following simple steps. Dealing with environment issues is the most difficult and frustrating part when you are trying to prove your concept.

  1. Install monit:
    sudo apt-get install monit
    
  2. Using sudo edit /etc/default/monit to setup automatic monit startup
    #startup=1
    START=yes
    
  3. Using sudo edit /etc/monit/monitrc as per the content below. Customize your web interface password, an IP allowed to connect via WEB (if you are a GUI guy), email addressee for alerts and mailserver. The rest of the settings should be OK for you. Note that the only check I am providing is checking Google availability just as a proof of concept. The web is full of samples to monitor anything in your servers
    ##################################################################
    
    # CUSTOM MONIT SETTINGS
    
    ##################################################################
    
    set mail-format {
    
      subject: [ $SERVICE ] $EVENT - $DATE
    
      message: Action: $ACTION, Description: $DESCRIPTION, Service: $SERVICE, Tested From Host: $HOST }
    
    # number of seconds between monit checks
    
    set daemon 60
    
    # Location of the monit log file
    
    # /var/log/messages is just ok
    
    # set logfile /var/log/monit.log
    
    set logfile syslog facility log_daemon
    
    # Change to the email address you want alerts to be sent to
    
    set alert nestorurquizaappmon@nestorurquiza.com
    
    # Port that the monit status page can be viewed
    
    set httpd port 2812
    
    # Allow localhost to connect
    
    allow localhost
    
    # The next line must be included to allow access from your computer
    
    # change the number to what your local ip is
    
    allow 0.0.0.0
    
    # The next line assigns a username:password combination to login.
    
    # Please change the password to something random.
    
    allow admin:myAdminPassword
    
    # Set the mailserver to send notification email.
    
    set mailserver mail.nestorurquiza.com
    
    ################################################################# 
    #REMOTE GOOGLE CHECKS
    ################################################################
    check host google-test with address google.com
      if failed port 80 proto http then alert
    group server
    
  4. Start, stop, restart, force reload as needed
    sudo /etc/init.d/monit start
    sudo /etc/init.d/monit stop
    sudo /etc/init.d/monit restart
    sudo /etc/init.d/monit force-reload
    
  5. See monit status, summary, unmonitor or monitor all as needed
    sudo monit status
    sudo monit summary
    sudo monit monitor all
    sudo monit unmonitor all
    
  6. Get extra help. Learn how to start or stop processes. Master monit!
    sudo monit -h
    
  7. If you prefer a GUI then get it with the below URL (Use the password you set)
    http://monit.server.address:2812/
    

Updating monit

Updating monit is easy. Just use this POB Recipe

Tuesday, June 21, 2011

ACL based security in JPA with jpasecurity: the next step after spring security

I had a clear need for ACL in my current project. Just protecting URLs is not enough and protecting method by method smells spaghetti code. Furthermore Spring solutions demand several hooks and still in my opinion they are still not addressing the real issue which is access control was removed from the database layer once ORM got mature but at the same time ORM did not provide a clean ACL solution.

If you are using JPA then you are in luck because jpasecurity project promises to resolve this limitation.

Rules are expressed in XML (I tested this so far) or Annotations (I had no luck with them so far). It allows granular access to CREATE, READ, UPDATE, DELETE operations based on roles and the current logged in user. It does that while wrapping all your JPA queries with the rules you specify. Basically you define access on let us say Client entity and every time Client entity appears in a JPA statement it wraps that statement adding the constraint. No need to say how powerful this is.

I used the trunk just because I was following up on some bugs that got corrected as I posted my questions. Probably you will get lucky and a stable 0.4.x release will be available by the time you decide to try it.

The examples here are based on a spring project using JPA + Hibernate + JTA + LDAP

Here are the main steps I followed:
  1. Edit your pom.xml
    <properties>
    ...
    <jpasecurity.version>0.4.0-SNAPSHOT</jpasecurity.version>
    ...
    </properties>
    ...
    <dependency>
              <groupId>net.sf.jpasecurity</groupId>
              <artifactId>jpasecurity-spring</artifactId>
              <version>${jpasecurity.version}</version>
              <exclusions>
               <exclusion>
                <artifactId>geronimo-ejb_3.1_spec</artifactId>
                <groupId>org.apache.geronimo.specs</groupId>
               </exclusion>
               <exclusion>
                <artifactId>geronimo-jpa_2.0_spec</artifactId>
                <groupId>org.apache.geronimo.specs</groupId>
               </exclusion>
              </exclusions>
    </dependency>
    ...
    
  2. If you need to debug some jpasecurity issues it is always useful to increase log level for the package
    log4j.logger.net.sf.jpasecurity=DEBUG
    
  3. I tried to use rules from annotations but the feature is still not well supported. I ended up putting my rules in XML. So here some samples. One of them as you can see quite complex:
    <security xmlns="http://jpasecurity.sf.net/xml/ns/security"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://jpasecurity.sf.net/xml/ns/security
                                  http://jpasecurity.sf.net/xml/ns/security/security_1_0.xsd">
    
      <persistence-unit name="nestorurquizaPersistenceUnit">
    <access-rule>GRANT CREATE READ        ACCESS TO Client c</access-rule>
    <access-rule>GRANT                    ACCESS TO Client c WHERE 'ROLE_ADMIN' IN (CURRENT_ROLES)</access-rule>
    <access-rule>GRANT ACCESS TO Client c WHERE c.id IN (SELECT cs.client.id FROM ClientStaffing cs, ClientStatus cst, Employee e WHERE e.email=CURRENT_PRINCIPAL AND cs.employee=e AND cs.client=c AND cs.endDate IS NULL AND ( cst.name &lt;&gt; 'Closed' OR cst.name IS NULL) )</access-rule>
      </persistence-unit>
    </security>
    
  4. In persistence.xml for your container:
    <!-- Comment the below if using jpasecurity -->
    <!--    <provider>net.sf.jpasecurity.persistence.SecurePersistenceProvider</provider>-->
    
        <!-- Uncomment the below if not using jpasecurity -->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
    ...
    
        <properties>
                <!-- Comment the below if not using jpasecurity -->
    <!--            <property name="net.sf.jpasecurity.persistence.provider" value="org.hibernate.ejb.HibernatePersistence" />-->
    <!--            <property name="net.sf.jpasecurity.security.authentication.provider" value="com.nestorurquiza.security.JpasecuritySpringAuthenticationProvider"/>-->
    
  5. Note the need for a custom SpringAuthenticationProvider. This is just a hook to guarantee that CURRENT_PRINCIPAL maps to the user email which is the username in LDAP.
    package com.nestorurquiza.security;
    
    import net.sf.jpasecurity.spring.authentication.SpringAuthenticationProvider;
    
    import org.springframework.security.authentication.AnonymousAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    
    public class JpasecuritySpringAuthenticationProvider extends SpringAuthenticationProvider{
        @Override
        public Object getPrincipal() {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null || (authentication instanceof AnonymousAuthenticationToken)) {
                return null;
            }
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            return userDetails.getUsername();
        }
    }
    
  6. I had to create a Custom Converter so Spring Binding works for forms. There is a bug I reported to Spring on this regard but anyway here is the workaround:
    package com.nestorurquiza.converter;
    
    import net.sf.jpasecurity.SecureObject;
    
    import org.springframework.core.convert.converter.Converter;
    
    public class SecureObjectToStringConverter implements Converter {
    
        @Override
        public String convert(SecureObject source) {
            return (source != null ? source.toString() : null);
        }
        
    }
    
  7. Then we need a custom ConversionServiceFactoryBean
    package com.nestorurquiza.converter;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.convert.support.GenericConversionService;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.format.support.FormattingConversionServiceFactoryBean;
    
    /**
     * Not being used.
     * @author jia
     */
    public class CustomConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {
    
        @Autowired
        private GenericConversionService genericConversionService;
        
        @Override
        protected void installFormatters(FormatterRegistry registry) {
            super.installFormatters(registry);
            //registry.addConverter(new BooleanToStringConverter());
            
            //Using org.springframework.format.support.FormattingConversionServiceFactoryBean from the xml declaration will not work
            //registry.addConverter(new SecureObjectToStringConverter());
            genericConversionService.addConverter(new SecureObjectToStringConverter());
        }
    }
    
    
  8. Then configure spring servlet with the necessary bean
    <!-- Registering custom ConversionService --> 
        <bean id="conversionService" class="com.nestorurquiza.converter.CustomConversionServiceFactoryBean" />
        <mvc:annotation-driven conversion-service="conversionService" />
        <!-- The below will not work at least for Binding --> 
        <!-- <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> 
            <property name="converters"> 
                <list> 
                    <bean class="com.nestorurquiza.converter.SecureObjectToStringConverter"/> 
                </list> 
            </property> 
        </bean> 
        -->
    
  9. Include the jpasecurity taglib for access rules in JSP
    <%-- Comment the below if not using jpasecurity --%>
    <%--@ taglib prefix="access" uri="http://jpasecurity.sf.net/access" --%>
    
  10. Use rules as needed in JSP
    <access:updating entity="client">
           <security:authorize url="/client/${client.id}/edit"><a href="<spring:url value="/client/${client.id}/edit?ctoken=${sessionScope.ctoken}"/>"><spring:message code="edit" /></a></security:authorize>
        </access:updating>
    
  11. Typical response when security is violated:
    java.lang.SecurityException: The current user is not permitted to update the specified object of type com.nestorurquiza.model.Client
    
  12. Some Jpasecurity limitations so far:
    • Does not accept CONCAT function so we must pass the percentages for the LIKE clauses within the parameters (which is best practice anyway)
    • count(*) is not supported. Use the entity alias instead like "SELECT count(c) FROM Client c"
    • LOWER and CONCAT functions are not supported but probably you can live without them and the less functions the best performance.
  13. Existing JUnit tests will fail if you enable jpasecurity and do not use a user with valid roles in the current context. The way you correct them is presented below:
    ...
    String adminEmail = "Admin.User@nestorurquiza.com";
    injectCurrentUser(adminEmail, Roles.ROLE_ADMIN);
    …
    private void injectCurrentUser(String email, String role) {
            TestingAuthenticationToken token = new TestingAuthenticationToken(
                    email, email, new GrantedAuthority[]{
                        new GrantedAuthorityImpl(role)});       
            SecurityContextHolder.getContext().setAuthentication(token);
    }
    

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; 
}

Followers