Tuesday, November 27, 2012

Autowiring optional properties with @Value

Spring provides an easy way to autowire property value into a bean. Let's say there is a system property configFileLocation. To access this property in a bean is as simple as:
@Value("#{systemProperties.configFileLocation}")
private String configFileLocation;
That's all fine and dandy. But what if the property is optional and is not always there? Spring will not like that and will let you know that by throwing:

org.springframework.beans.factory.BeanCreationException: 
Could not autowire field

And while @Autowire construct provides a required attribute, which can be set to false, alas @Value does not.

This issue is discussed in Captain Debug's Blog where he offers several work-a-rounds to this issue, but all of them seem to be quite cumbersome. So upon digging a little deeper, I stumbled on a Stack Overflow thread where one of the answers suggests following quite simple solution using SpEL Elvis operator:
@Value("#{systemProperties.getProperty('configFileLocation') ?: ''}")
private String configPath;

What this means is that if property exists, its value will be assigned to configPath, if it doesn't exist configPath will be set to blank.

Monday, November 26, 2012

Customizing ICE Faces ACE Dataexporter

Recently, I was presented with a requirement to export data from ICE Faces ACE Data Table to a CSV file. ICE Faces provides an out of the box component - Data Exporter to do just that, however in my case the requirement was to save the CSV file to a server location. Unfortunately, Data Exporter does not provide this option, instead sending the file back to the user in a response. Here is my workaround (which can be applied to other supported export types such as pdf and excel):

First, ace datatable needs to bound to the backing bean:

 
  <ace:dataTable id="summaryTable" var="summary"   
          value="#{summaryReportBean.summaryList}"   
         binding="#{summaryReportBean.summaryDataTable}">  
  . . .  
  </ace:dataTable>  

And the corresponding bean:

 
 import java.util.List 
 import org.springframework.beans.factory.annotation.Autowired; 
 import org.springframework.context.annotation.Scope;  
 import org.springframework.stereotype.Component;  
 import com.jv.service.SummaryReportService;
 import org.icefaces.ace.component.datatable.DataTable;

 public class SummaryReportBean {  
 @Autowired
 private SummaryReportService reportService;      
 private DataTable summaryDataTable;  
      
 public List<ReportLine> getSummaryList() {  
  return reportService.generateReport();  
 } 
     
 public DataTable getSummaryReportTable() {  
  return summaryReportTable;  
 }  
        
 public void setSummaryReportTable(DataTable summaryReportTable) {  
  this.summaryReportTable = summaryReportTable;  
 }     
}


Now, we'll add a button which will trigger the CSV export file to be generated:

 
  <h:commandButton id="SummaryReportExportButton"
      value="Export Summary Report to CSV" 
      rendered="#{not empty summaryReportBean.summaryList}"
      actionListener="#{summaryReportBean.exportSummaryReportToCSV}" /> 


And here is the code to generate the CSV file:


 
import javax.faces.context.FacesContext;
import org.apache.commons.io.IOUtils;
import org.icefaces.ace.component.dataexporter.CSVExporter;
import org.icefaces.ace.component.datatable.DataTable;
import org.icefaces.application.ResourceRegistry;

...

public void generateReport(ActionEvent event) throws IOException {
 CSVExporter exporter = new CSVExporter();
 String resourcePath = 
      exporter.export(FacesContext.getCurrentInstance(), 
                 summaryDataTable, null, false, 
                 null, null, null, null, true, false, false);

     InputStream inputStream = 
         ResourceRegistry.getResourceByPath(
      FacesContext.getCurrentInstance(), resourcePath)
    .getInputStream();

  
 String report = IOUtils.toString(inputStream));
 
 saveReportToFile(report);
}

CSVExporter is a class ICEFaces uses to generate CSV exports. Here is the export method signature:
public String export(FacesContext facesContext, 
                     DataTable table, 
                     String filename, 
                     boolean pageOnly, 
                     int[] excludeColumns, 
                     String encodingType, 
                     MethodExpression preProcessor,
                     MethodExpression postProcessor, 
                     boolean includeHeaders, 
                     boolean includeFooters, 
                     boolean selectedRowsOnly) throws IOException { 

CVSExporter wraps the resulting CSV text into org.icefaces.ace.component.dataexporter.ExporterResource and puts it into org.icefaces.application.ResourceRegistry. So all this is needed to do is to fetch the resource from the registry, get the InputStream and write it to a desired server location.