Extract method refactor with Java 8 default method

Java 8 default methods can be a handy tool for refactoring interface contracts. You can use this trick in case you want to add a new interface method that can be expressed in terms of an existing one. You don’t want to change the implementors of the interface (at least for now).

Let’s take an example on a interface (Loader) with a mis-defined contract. Instead of getting levels where a Loader can be applied to, I wanted the loader to answer this question directly.

The initial code was as follows:

public interface Loader {
  void load(Page page);

  List<Level> getLevelsToBeAppliedOn();
}

boolean shouldBeApplied(Loader loader, Level level) {
    return loader.getLevelsToBeAppliedOn().stream().anyMatch(level::equals);
}

After the refactor, the Loader interface gets a new default method, shouldBeAppliedOn that delegates to the existing method, getLevelsToBeAppliedOn:

public interface Loader {
  void load(Page page);

  List<Level> getLevelsToBeAppliedOn();

  default boolean shouldBeAppliedOn(Level other) {
    return getLevelsToBeAppliedOn().stream().anyMatch(other::equals);
  }
}

The client code is simplified:

boolean shouldBeApplied(Loader loader, Level level) {
  return loader.shouldBeAppliedOn(level);
}

Now this method doesn’t make our intent any clearer and can be safely inlined.

This refactoring didn’t break the implementors of the Loader interface. The next step would be to implement the shouldBeAppliedOn method in all Loader derivates. When done, I could convert the default method to an abstract method (i.e. remove its body) and remove the original method (getLevelsToBeAppliedOn).

Things I learned about ReactiveX and RxJava

Things I learned about ReactiveX and RxJava

Some loose notes about what I learned about ReactiveX and RxJava while developing Vitamin-Rx, a simplified clone of Vitamin-R 2.

Hot and cold Observables

When creating an Interval Observable, I thought it will start ticking (emitting) immediately after creation. Wrong! There are hot and cold Observables. Hot Observables start firing events immediately. Cold Observables, only when there is at least one subscriber. Interval is cold – you have to connect an Observer first.

Operators

Docs and marble diagrams are the best source of information about transforming Observables.

Pure streams and side effects

When you connect an Observer to an Observable, every item emitted by the source is passed to the onNext Observer method. There are no restrictions on what you can put in the onNext handler. On the other hand, there are do callbacks, which can act similarly to onNext.

What’s the best practice? Implement onNext handlers as pure functions. Actions with side-effects go into doOnNext callbacks.

In my pet project, TimeSlice class creates a tick stream transformed previously with pure functions. Main class adds impure console and Growl notifications to the stream in do callbacks. In this way I can unit test the Observable produced by the TimeSlice class using the provided TestScheduler (more on this in the next section).

Testing

Want to be a master of time? Test your Observables with TestScheduler. You will be able to advance time and gather all items produced by the Observable under test. Check the TimeSliceTest class for usage examples.

Backpressure

Backpressure is a way to deal with an Observable that produces items too rapidly (or too slowly).

In my case I wanted to pause the interval Observable and resume its activity. I didn’t find a direct way (i.e. through any of RxJava interfaces), so I asked on Stack Overflow. The solution consists in filtering the Observable by a boolean “semaphore” toggled from outside.

Android form conversion and validation with Option monad

What I want to do

  • Given a form in an Android activity or dialog, convert form values (java.lang.Strings) to domain classes and validate if they fulfill business constraints
  • Don’t validate if there is a conversion error
  • Chain validators
  • Perform complex validations (e.g. of two dependent fields)

Solution with Option monad

I encapsulated a form value coming from a visual component (it can be an EditText, Spinner, whatever) in the FormValue monadic type. This is a straight implementation of the Option monad with two subtypes:

  • Some containing the valid value
  • None containing the error description

See the source code below for the implementation:

public abstract class FormValue<T> {
    public static <T> FormValue<T> some(T value) {
        return new Some(value);
    }

    public static <T> FormValue<T> none(String error) {
        return new None(error);
    }

    private FormValue() {}

    public abstract boolean hasValue();
    public abstract T value();

    public abstract boolean hasError();
    public abstract String error();

    public abstract FormValue<T> validateWith(Validator<T> validator);

    public static class Some<T> extends FormValue<T> {
        private T value;

        private Some(T value) { this.value = value; }

        @Override
        public boolean hasValue() { return true; }
        @Override
        public T value() { return value; }

        @Override
        public boolean hasError() { return false; }
        @Override
        public String error() {
            throw new NotImplementedException("Not an error");
        }

        @Override
        public FormValue<T> validateWith(Validator<T> validator) {
            return validator.validate(value);
        }
    }

    public static class None<T> extends FormValue<T> {
        private String error;

        private None(String error) { this.error = error; }

        @Override
        public boolean hasValue() { return false; }
        @Override
        public T value() {
            throw new NotImplementedException("Not a value");
        }

        @Override
        public boolean hasError() { return true; }
        @Override
        public String error() { return error; }

        @Override
        public FormValue<T> validateWith(Validator<T> validator) {
            return this;
        }
    }
}

How does it work?

Conversion

First, we have to extract the raw value stored in a widget and convert it to a domain representation wrapped in FormValue. We do this with the ViewValueExtractor interface:

public interface ViewValueExtractor<T> {
    ViewValueExtractor<T> withMessage(String message);

    FormValue<T> extractValue();
}

Implementations of this interface depend on the widget. In the simplest form, it extracts a String value from a TextView doing no conversion (and thus not raising any errors):

public class StringExtractor implements ViewValueExtractor<String> {
    private TextView view;

    public StringExtractor(TextView view) { this.view = view; }

    public ViewValueExtractor<String> withMessage(String message) {
        return this;
    }

    public FormValue<String> extractValue() {
        return FormValue.some(view.getText().toString());
    }
}

Another example converts a text field to the SSN class, requiring AAA-GG-SSSS format:

public class SSN {
    // getters and setters omitted for brevity
    public int areaNumber;
    public int groupNumber;
    public int serialNumber;

    public SSN(int areaNumber, int groupNumber, int serialNumber) {
        this.areaNumber = areaNumber;
        this.groupNumber = groupNumber;
        this.serialNumber = serialNumber;
    }
}

public class SSNExtractor implements ViewValueExtractor<SSN> {
    private TextView view;
    private String message;

    public SSNExtractor(TextView view) {
        this.view = view;
    }

    public ViewValueExtractor<SSN> withMessage(String message) {
        this.message = message;
        return this;
    }

    public FormValue<SSN> extractValue() {
        String text = view.getText().toString();
        String[] ssnParts = text.split("-");
        if (ssnParts.length != 3) {
            return FormValue.none(message);
        }
        try {
            return FormValue.some(
                new SSN(
                    Integer.valueOf(ssnParts[0]), 
                    Integer.valueOf(ssnParts[1]), 
                    Integer.valueOf(ssnParts[2])
                )
            );
        } catch (NumberFormatException e) {
            return FormValue.none(message);
        }
    }
}

Generally speaking, you need to provide some way to pull out the value from a visual component and turn it into Some if the conversion was successful or None (with an error message).

Validation

We have already an object representing a value from our solution domain; now it’s time to validate it. We do it implementing a Validator:

public interface Validator<T> {
    Validator<T> withMessage(String message);

    FormValue<T> validate(T value);
}

It can be a generic validator:

public class GreaterThatOrEqualValidator<T extends Comparable<T>> implements Validator<T> {
    private T minValue;
    private String message;

    GreaterThatOrEqualValidator(T minValue) {
        this.minValue = minValue;
    }

    public Validator<T> withMessage(String message) {
        this.message = message;
        return this;
    }

    public FormValue<T> validate(T value) {
        if (value.compareTo(minValue) >= 0) {
            return FormValue.some(value);
        }

        return FormValue.none(message);
    }
}

Or a domain-specific validator, e.g. for our Social Security Number:

public class SSNValidator implements Validator<SSN> {
    private String message;

    public Validator<SSN> withMessage(String message) {
        this.message = message;
        return this;
    }

    public FormValue<SSN> validate(SSN value) {
        if (areaValid(value.areaNumber) 
            && groupValid(value.groupNumber) 
            && serialValid(value.serialNumber)) {
            return FormValue.some(value);
        }

        return FormValue.none(message);
    }

    private boolean areaValid(int areaNumber) {
        return areaNumber >= 0 && areaNumber <= 999;
    }

    private boolean groupValid(int groupNumber) {
        return groupNumber > 0 && groupNumber <= 99;
    }

    private boolean serialValid(int serialNumber) {
        return serialNumber > 0 && serialNumber <= 9999;
    }
}

If you go back to the implementation of FormValue.validateWith, you may notice that Validator.validate will be called only for Some value. In case of None (resulting e.g. from a wrong conversion), validateWith returns the same None object.

Complete example – conversion + validation

We convert the SSN field to SSN and check if it’s valid. In case of errors (during conversion or validation), an error message will be displayed in a Toast.

public class MonadicValidatorDemoActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.validate).setOnClickListener(
            new View.OnClickListener() {
                public void onClick(View v) {
                    validateForm();
                }
            }
        );
    }

    private void validateForm() {
        TextView ssnField = (TextView) findViewById(R.id.ssn);
        String wrongSSNFormatMsg = getString(R.string.wrongSSNFormat);
        String invalidSSNMsg = getString(R.string.invalidSSN);
        
        FormValue<SSN> ssn = new SSNExtractor(ssnField).withMessage(wrongSSNFormatMsg)
            .extractValue()
            .validateWith(new SSNValidator().withMessage(invalidSSNMsg));

        if (ssn.hasValue()) {
            Toast.makeText(this, "SSN: " + ssn.value(), Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, "Error: " + ssn.error(), Toast.LENGTH_LONG).show();
        }
    }
}

Chained and dependent validation

You can combine validators, adding more validateWith calls. Dependent validation is also possible:

  • either by implementing a validator factory accepting a FormValue and implementing a no-op or identity validator (NoOpValidator) – see the code example below
  • or by implementing a Validator verifying a FormValue, e.g. GreaterThatOrEqualValidator<T extends Comparable> implements Validator<FormValue>
private void validateForm() {
    // Field and message finders omitted
    FormValue<Date> startDate = new DateExtractor(startDateField).withMessage(invalidStartDateFormat)
        .extractValue()
        .validateWith(greaterThatOrEqual(today()).withMessage(startDateInThePast));

    FormValue<Date> endDate = new DateExtractor(endDateField).withMessage(invalidEndDateFormat)
        .extractValue()
        .validateWith(greaterThatOrEqual(today()).withMessage(endDateInThePast));
        .validateWith(greaterThatOrEqual(startDate).withMessage(startDateAfterEndDate));

    // Do something with form values and validation errors
}

private <T extends Comparable<T>> Validator<T> greaterThatOrEqual(T value) {
    return new GreaterThatOrEqualValidator<T>(value);
}

private <T extends Comparable<T>> Validator<T> greaterThatOrEqual(FormValue<T> value) {
    if (value.hasValue()) {
        return new GreaterThatOrEqualValidator<T>(value.value());
    }

    return new NoOpValidator<T>();
}
public class NoOpValidator<T> implements Validator<T> {
    private String message;

    @Override
    public Validator<T> withMessage(String message) {
        this.message = message;
        return this;
    }

    public FormValue<T> validate(T value) {
        return FormValue.some(value);
    }
}

Is it really a monad?

If a type wants to be a monad, it has to have:

  • unit operation that “wraps” a value with monad. It can be a constructor or a factory method. In our case FormValue.some represents the unit operation
  • bind operation transforming the monad into a next monad, exposing its internal value for a transformation function. FormValue.validateWith does this. Because Java doesn’t have first-class functions, we represent the validateWith operation argument as a method object – Validator

Furthermore, a monad must follow three monadic laws:

  1. identity – transforming to unit doesn’t change a monad
  2. m.bind { x -> unit(x) } ≡ m

    Previously mentioned NoOpValidator implements such a function:

    public FormValue<T> validate(T value) {
        return FormValue.some(value);
    }
    

    In case of Some, calling validateWith (bind method) with this Validator will return a new Some monad holding the same value. None‘s implementation returns always the same monad instance. Hence the first law is fulfilled.

  3. unit – unit must preserve the value inside the monad
  4. unit(x).bind(f) ≡ f(x)

    unit(x) corresponds to FormValue.some(x). Result of the execution of Some.validateWith is equivalent to the execution of the provided Validator:

    public FormValue<T> validateWith(Validator<T> validator) {
        return validator.validate(value);
    }
    
  5. associativity – order of monad composition doesn’t matter
  6. m.bind(f).bind(g) ≡ m.bind{ x -> f(x).bind(g) }

    Since None‘s validateWith returns always none (ignoring the bind function), it satisfies the associativity law.
    For Some it’s not so obvious. Let’s expand the left part of the theorem:

    some.validateWith(validator1).validateWith(validator2) => validator1.validate(value).validateWith(validator2)
    

    In order to implement the function in Java from the right side of the 3rd monadid law, we create an anonymous Validator:

    some.validateWith(
        new Validator<T>() {
            public FormValue<SSN> validate(SSN value) {
                validator1.validate(value).validateWith(validator2)
            }
        }
    );
    

    As you can see the result of the validation in the anonymous class is the same as of the expression expanded above.

    Conclusions

    Monads look abstract, esoteric, and, I admit, a little scary. Although from the functional programming domain, they can be implemented in imperative languages. They solve real-life problems (converstion and validation) in an elegant manner (we chained converters and validators with few ifs and no null checking).

Smooks processing recipies

Introduction

In one of customer projects I had a requirement to import CSV, fixed length and Excel files in different formats and store records in the database. I chose Smooks to accomplish this task.

Smooks is a Java framework to read, process and transform data from various sources (CSV, fixed length, XML, EDI, …) to various destinations (XML, Java objects, database). It convinced me because:

  • it brings out-of-the-box components to read CSV and fixed length files
  • it integrates smoothly with an ORM library (Hibernate, JPA)
  • processing is configured using an XML configuration file – you need only few lines of code to do the transformations
  • extensibility – implementing a custom Excel reader was relatively easy
  • low added filtering overhead – reading 100.000 CSV lines and storing them in the database using Hibernate took us less than 30 seconds

During the development I had to overcome some hurdles imposed by Smooks processing model. In this post I would like to share my practical experience I gained working with Smooks. First, I’m going to present a sample transformation use case with requirements similar to a real-world assignment. Then I will present solutions to these requirements in a ‘how-to’ style.

Use case

We are developing a ticketing application. The heart of your application is Issue class:

We have to write an import and conversion module for an external ticketing system. Data comes in the CSV format (for the sake of simplicity). The domain model of the external system is slightly different than ours; however, issues coming from the external issue tracker can be mapped to our Issues.

External system exchange format defines the following fields: description, priority, reporter, assignee, createdDate, createdTime, updatedDate, updatedTime. They should be mapped to our Issue in the following manner:

  • description property – description field
  • This is a simple Smooks mapping. No big issue.

  • project property – there is no project field. Project should be assigned manually
  • A constant object (from our domain model) must be passed to Smooks engine to be used in Java binding.
    See Assign constant object to a property.

  • priority property – priority field; P1 and P2 priorities should be mapped to Priority.LOW, P3 to Priority.MEDIUM, P4 and P5 to Priority.HIGH
  • This mapping could be done using an MVEL expression. However, we want to encapsulate this logic in a separate class that can be easily unit-tested. See Use external object to calculate property value

  • involvedPersons property – reporter field plus assignee field if not empty (append assignee using ‘;’ separator)
  • Set compound properties in Java binding will show how to achieve it.

  • created property – merge createdDate and createdTime fields
  • updated property – merge updatedDate and updatedTime fields
  • In Set datetime from date and time fields, two strategies will be presented.

    Before diving into details, I’m going to present the final Smooks configuration and the invocation code of the transformation (as JUnit 4 test). Later on, in each recipe, I will explain the XML configuration and Java code fragments relevant to that recipe.

    The remaining classes (Issue, Priority, Project, IssuePrioritizer) are not included in the text. You can browse online the source code in GitHub. To get your local copy, clone the Git repository:


    git clone git://github.com/mgryszko/blog-smooks-recipies.git


    smooks-config.xml

    <?xml version="1.0"?>
    <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
        xmlns:csv="http://www.milyn.org/xsd/smooks/csv-1.3.xsd"
        xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.3.xsd">
    
        <params>
            <param name="stream.filter.type">SAX</param>
        </params>
    
        <csv:reader fields="description,priorityCode,reporter,assignee,createdDate,createdTime,updatedDate,updatedTime" skipLines="1"/>
    
        <jb:bean beanId="transformedProps" class="java.util.HashMap" createOnElement="csv-record">
            <jb:value property="@reporter" data="csv-record/reporter" />
            <jb:value property="@assignee" data="csv-record/assignee" />
            <jb:value property="@updatedDate" data="csv-record/updatedDate"/>
            <jb:value property="@updatedTime" data="csv-record/updatedTime"/>
        </jb:bean>
    
        <jb:bean beanId="issues" class="java.util.ArrayList" createOnElement="csv-set">
            <jb:wiring beanIdRef="issue" />
        </jb:bean>
    
        <jb:bean beanId="issue" class="com.tsl.smooks.model.Issue" createOnElement="csv-record">
            <jb:value property="description" data="csv-record/description" />
            <jb:wiring property="project" beanIdRef="project" />
            <jb:expression property="priority" execOnElement="csv-record/priorityCode">
                prioritizer.assignPriorityFromCode(_VALUE)
            </jb:expression>
            <jb:expression property="involvedPersons" execOnElement="csv-record">
                transformedProps["reporter"]
                    + (org.apache.commons.lang.StringUtils.isNotBlank(transformedProps["assignee"]) ? ";" + transformedProps["assignee"] : "")
            </jb:expression>
            <jb:value property="createdDatePart" decoder="Date" data="csv-record/createdDate">
                <jb:decodeParam name="format">yyyy-MM-dd</jb:decodeParam>
            </jb:value>
            <jb:value property="createdTimePart" decoder="Date" data="csv-record/createdTime">
                <jb:decodeParam name="format">HH:mm</jb:decodeParam>
            </jb:value>
            <jb:expression property="updated" execOnElement="csv-record">
                updated = transformedProps["updatedDate"] + " " + transformedProps["updatedTime"];
                new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm").parse(updated)
            </jb:expression>
        </jb:bean>
    </smooks-resource-list>
    


    SmooksRecipiesTest

    package com.tsl.smooks;
    
    // imports hidden
    
    public class SmooksRecipiesTest {
    
        private static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    
        private Source importedIssues = new StringSource(
            "description,priority,reporter,assignee,createdDate,createdTime,updatedDate,updatedTime\n"
                + "Added phased initialization of javabean cartridge,P1,Ataulfo,Teodorico,2010-10-01,13:10,2010-10-10,20:01\n"
                + "Processing recursive tree like structures with the Javabean Cartridge,P3,Eurico,,2010-10-02,07:15,2010-11-15,09:45"
        );
    
        private Smooks smooks;
        private ExecutionContext executionContext;
    
        private List<Issue> expIssues;
        private Project expProject = new Project("Smooks");
    
        @Before
        public void initSmooks() throws Exception {
            smooks = new Smooks(getResourceFromClassLoader("smooks-config.xml"));
            executionContext = smooks.createExecutionContext();
            executionContext.getBeanContext().addBean("project", expProject);
            executionContext.getBeanContext().addBean("prioritizer", new IssuePrioritizer());
        }
    
        private InputStream getResourceFromClassLoader(String name) {
            return getClass().getClassLoader().getResourceAsStream(name);
        }
    
        @Before
        public void createExpIssues() throws Exception {
            expIssues = Arrays.asList(
                new Issue("Added phased initialization of javabean cartridge", expProject, Priority.LOW,
                    "Ataulfo;Teodorico", DATETIME_FORMAT.parse("2010-10-01 13:10"), DATETIME_FORMAT.parse("2010-10-10 20:01")
                ),
                new Issue(
                    "Processing recursive tree like structures with the Javabean Cartridge", expProject, Priority.MEDIUM,
                    "Eurico", DATETIME_FORMAT.parse("2010-10-02 07:15"), DATETIME_FORMAT.parse("2010-11-15 09:45")
                )
            );
        }
    
        @Test
        public void process() throws Exception {
            smooks.filterSource(executionContext, importedIssues);
    
            List<Issue> issues = (List<Issue>) executionContext.getBeanContext().getBean("issues");
            assertEquals(expIssues, issues);
        }
    }
    


    Assign a constant object (from your domain model) to a property

    According to the Smooks manual, bean context is the place where JavaBean cartridge puts newly created beans. We can add our own bean (Project):

    executionContext = smooks.createExecutionContext();
    executionContext.getBeanContext().addBean("project", new Project("Smooks"));
    

    … and reference it in the Java binding configuration:

    <jb:bean beanId="issue" class="com.tsl.smooks.model.Issue" createOnElement="csv-record">
        ....
        <jb:wiring property="project" beanIdRef="project" />
        ...
    </jb:bean>
    


    Use an external object to calculate property value

    Similar to the previous tip we add an additional bean (IssuePrioritizer) to the bean context:

    executionContext = smooks.createExecutionContext();
    executionContext.getBeanContext().addBean("prioritizer", new IssuePrioritizer());
    

    … and define an MVEL expression for the property. The MVEL expression uses the bean and references the value being processed (in this case coming from the CSV reader) by the implicit _VALUE variable:

    <jb:bean beanId="issue" class="com.tsl.smooks.model.Issue" createOnElement="csv-record">
        ....
        <jb:expression property="priority" execOnElement="csv-record/priorityCode">
            prioritizer.assignPriorityFromCode(_VALUE)
        </jb:expression>
        ...
    </jb:bean>
    


    Set compound properties in Java binding

    It is not possible to map directly two source fields to a Java bean property. Java bindings with and are executed on a SAX visitAfter event bound to to a single XML element/CSV field. We have to define a binding for a helper Map bean with the fields we want to merge:

    <jb:bean beanId="transformedProps" class="java.util.HashMap" createOnElement="csv-record">
        <jb:value property="@reporter" data="csv-record/reporter" />
        <jb:value property="@assignee" data="csv-record/assignee" />
        ...
    </jb:bean>
    

    … and use an MVEL expression that concatenates two fields using the helper map bean (transformedProps):

    <jb:bean beanId="issue" class="com.tsl.smooks.model.Issue" createOnElement="csv-record">
        ...
        <jb:expression property="involvedPersons" execOnElement="csv-record">
            transformedProps["reporter"]
                + (org.apache.commons.lang.StringUtils.isNotBlank(transformedProps["assignee"]) ? ";" + transformedProps["assignee"] : "")
        </jb:expression>
        ...
    </jb:bean>
    


    Set datetime from date and time fields

    In this transformation we have to both merge and convert values of two fields.

    In the first solution, we create a separate setter for the date and time part in the target Issue class (Smooks uses setters in Java binding):

    public class Issue {
        ...
        public void setCreatedDatePart(Date createdDatetime) {
            createCreatedIfNotInitialized();
            copyDatePart(createdDatetime, created);
        }
    
        public void setCreatedTimePart(Date createdDatetime) {
            createCreatedIfNotInitialized();
            copyTimePart(createdDatetime, created);
        }
        ...
    }
    

    … and then use a standard value binding with date decoder:

    <jb:bean beanId="issue" class="com.tsl.smooks.model.Issue" createOnElement="csv-record">
        ...
        <jb:value property="createdDatePart" decoder="Date" data="csv-record/createdDate">
            <jb:decodeParam name="format">yyyy-MM-dd</jb:decodeParam>
        </jb:value>
        <jb:value property="createdTimePart" decoder="Date" data="csv-record/createdTime">
            <jb:decodeParam name="format">HH:mm</jb:decodeParam>
        </jb:value>
        ...
    </jb:bean>
    

    The advantage of this approach is that you make use of Smooks decoder infrastructure. You can configure the transformation with your own decoders (e.g. custom java.util.Date allowing to specify multiple date formats). If you are using the built-in DateDecoder, you can catch and handle a standard DataDecodeException.

    The disadvantage is that you have to change your domain model code. New methods add complexity and must be unit tested, especially in cases when only one of partial setter is called.

    In the second solution, you define a binding for a helper Map bean with the date and time fields. In the right binding you use an MVEL expression concatenating date and time strings and converting them to Date (e.g. using a java.text.SimpleDateFormat instance):

    <jb:bean beanId="issue" class="com.tsl.smooks.model.Issue" createOnElement="csv-record">
        ...
        <jb:expression property="updated" execOnElement="csv-record">
            updated = transformedProps["updatedDate"] + " " + transformedProps["updatedTime"];
            new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm").parse(updated)
        </jb:expression>
        ...
    </jb:bean>
    

    The advantages of the first solution are disadvantages of the second one. You don’t touch your Java classes. It is simple – you have to specify only the Smooks configuration. In case of handling of many date/time formats and their combinations, the MVEL expression defining the conversion can become complicated. In case of an exception, you won’t get DataDecodeException, but an ugly, generic ExpressionEvaluationException.

    Conclusions

    Smooks is a great library that will save you writing a lot of code in case of processing many different formats. With a few lines of code and the XML configuration you will be able to read a file and persist its contents in the database using your favourite ORM framework.

    However, Smooks processing model and its usage in built-in cartridges make sometimes difficult to configure the transformation for a real world requirement. The information provided in the user guide is sometimes scarce and unclear. I hope these practical cases will help you use Smooks Java bean mappings and MVEL expressions more effectively.

    Comments

    This article is published also on my employer’s blog. Please post all comments there.

Use custom fonts in JasperReports PDF exporter

I was using custom font in a customer report embedded in the web application. The font (available in the operating system) was displayed correctly in HTML, RTF and Excel export. However, in the generated PDF text elements were displayed using Arial font.

The solution is to wrap required fonts as JasperReports Font Extensions. Font TTF files can be included in the same project generating Jasper reports or, a more elegant solution, in a separate JAR file.

I found an interesting blog post Fonts in JasperServer about providing custom fonts. The author is configuring the font extensions using Spring. However, you can do it in a simpler way.

First, install fonts in iReport and let create it the required configuration. In iReport, go to Tools -> Options, click on iReport group and go to Fonts tab. Here are some screenshots from the installation wizard:

iReport creates font extensions configuration and copies the recently installed font into the iReport installation directory/ireport/fonts directory (C:\Program Files (x86)\iReport-3.7.2\ireport\fonts in my case). You should find the following files there:

  • jasperreports_extension.properties – it configures net.sf.jasperreports.extensions.ExtensionsRegistryFactory as net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory and points to the font family configuration:
  • net.sf.jasperreports.extension.registry.factory.fonts=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
    net.sf.jasperreports.extension.simple.font.families.ireport=irfonts.xml
    
  • irfonts.xml – font family configuration:
  • 
    <fontfamilies>
       <fontfamily name="Calibri">
           <normal><!--[CDATA[calibri.ttf]]--></normal>
           <bold><!--[CDATA[calibrib.ttf]]--></bold>
           <italic><!--[CDATA[calibrii.ttf]]--></italic>
           <bolditalic><!--[CDATA[calibriz.ttf]]--></bolditalic>
           <pdfembedded><!--[CDATA[true]]--></pdfembedded>
       </fontfamily>
    </fontfamilies>
    
  • one or more TTF font files

All files found there that need to be packed in the JAR; let’s use Maven to create it:

  1. Create the Maven project directory (e.g. jasperreports-font-extensions) with src/main/resources subdirectory.
  2. Copy all files from iReport installation directory/ireport/fonts to Maven font extensions project directory/src/main/resources.
  3. Add pom.xml:
  4. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelversion>4.0.0</modelversion>
        <groupid>net.gryszko</groupid>
        <artifactid>jasperreports-font-extensions</artifactid>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    </project>
    
  5. Build the JAR and install it in the local Maven repository:
  6. mvn install

The final step is to reference the newly created JAR as dependency in your reports project:

<project>
    ...
    <dependencies>
        <dependency>
            <groupid>net.gryszko</groupid>
            <artifactid>jasperreports-font-extensions</artifactid>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        ...
    </dependencies>
    ...
</project>

After that, the fonts contained it JAR will be available to JasperReports when generating a PDF report.

Update:
Just before publishing my post, Matt Dahlmann, author of the published an update of his previous article mentioned in this post. Check Jaspersoft v3 Font Extensions for the more actual version.