Personal tools

java

Mar 08, 2013

Merging odf files using XDocReport and XPages

LET'S MERGE TOGHETER!

Merging odf files using XDocReport and XPages

Filed Under:

Often there is the need to create documents from templates, and the need to fill these templates with data available from other sources: let's take a look at this brief solution

In this scenario, we have a odf template to merge with data.

Working in the Lotus Domino environment, one way to achieve this goal could be a server-side component invoked by an XPagdies, to make the casting process: first, I need something in Java that could do the job for me, and after some search I came across XDocReport.

XDocReport means XML Document reporting

XDocReport provides Java API to merge XML document created with MS Office (docx, pptx) or OpenOffice (odt), LibreOffice (odt) with a Java model to generate report and convert it in another format (PDF, XHTML...).

Basicly, to make the template, main steps are:

  1. create a template document with MS Word (docx) or OpenOffice (odt, ods)
  2. compose the body of the document itself with variables to fill in
  3. use Velocity or Freemarker syntax to set variables to replace.


In this case, I opted to use Velocity inside an ODF file.

Now let's start by explaining how to set up the whole project.

Installing server libraries

External libraries (.jar) that serve the purpose have been installed in the installation directory of the Domino server (jvm/lib/ext path).

These are the files:

commons-collections-3.2.1.jar
commons-lang-2.4.jar
fr.opensagres.xdocreport.converter-1.0.0.jar
fr.opensagres.xdocreport.converter.odt.odfdom-1.0.0.jar
fr.opensagres.xdocreport.core-1.0.0.jar
fr.opensagres.xdocreport.document-1.0.0.jar
fr.opensagres.xdocreport.document.odt-1.0.0.jar
fr.opensagres.xdocreport.itext.extension-1.0.0.jar
fr.opensagres.xdocreport.template-1.0.0.jar
fr.opensagres.xdocreport.template.velocity-1.0.0.jar
itext-2.1.7.jar
odfdom-java-0.8.7.jar
org.odftoolkit.odfdom.converter-0.9.0.jar
org.odftoolkit.odfdom.converter.core-1.0.0.jar
org.odftoolkit.odfdom.converter.pdf-1.0.0.jar
org.odftoolkit.odfdom.converter.xhtml-1.0.0.jar
oro-2.0.8.jar
velocity-1.7.jar

Dont' forget an important security setting in java.policy file:

grant {
permission java.lang.RuntimePermission "getClassLoader";
permission java.lang.RuntimePermission "setContextClassLoader";
}

After that, restart HTTP server.

Server-side tools

To manage the whole process I used an HTML page with a button that calls (via Ajax) an XPages, that deals with the fusion of the data (HTML string) in the template. At the end, I create the PDF file using iText libraries.

Related to XPages, it only call the servlet in the afterPageLoad event:

com.redturtle.XDocReport.mergeAndPDF(
	facesContext.getExternalContext().getRequest(), 
	facesContext.getExternalContext().getResponse()
);

This is how it looks the package, which includes the servlet:

package

Finally, this is the servlet code:

package com.redturtle;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.odftoolkit.odfdom.converter.pdf.PdfConverter;
import org.odftoolkit.odfdom.converter.pdf.PdfOptions;
import org.odftoolkit.odfdom.doc.OdfTextDocument;

import fr.opensagres.xdocreport.core.document.SyntaxKind;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;

import lotus.domino.NotesException;
import lotus.domino.Session;

public class XDocReport {

	private static String VERSION = "XDocReport Version 1.0.0";
	static PrintWriter outClass = null;
	private static Session session = null;

	public static void mergeAndPDF (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, InterruptedException, NotesException {

                // NO DIIOP
		session=DominoAccess.getCurrentSession();
		try {			
			// 1) Load ODT file by filling Velocity template engine
			InputStream in = new FileInputStream("/tmp/template.odt");

			IXDocReport report = XDocReportRegistry.getRegistry().loadReport(in,TemplateEngineKind.Velocity);

			FieldsMetadata metadata = report.createFieldsMetadata();
			metadata.addFieldAsTextStyling("comments",SyntaxKind.Html,false);

			// 2) Create context Java model
			IContext context = report.createContext();
			context.put("comments", "<b>hello world</b>");
			
			// 3) Generate report by merging Java model with the ODT
			OutputStream out = new FileOutputStream(new File("/tmp/ODTProjectWithVelocity_Out.odt"));
			report.process(context, out);

			// 1) Load ODT into ODFDOM OdfTextDocument 
			in= new FileInputStream(new File("/tmp/ODTProjectWithVelocity_Out.odt"));
			OdfTextDocument document = OdfTextDocument.loadDocument(in);

			// 2) Prepare Pdf options
			PdfOptions options = PdfOptions.create();
			
			// 3) Convert OdfTextDocument to PDF via IText
			out = new FileOutputStream(new  File("/tmp/ODTProjectWithVelocityList_Out.pdf"));
			PdfConverter.getInstance().convert(document, out, options);
 
                        // Only manage response to Ajax call
		        response.setContentType("text/plain");
		        response.setCharacterEncoding("UTF-8");
			outClass = new PrintWriter(response.getWriter());
			printAgent("OK");
			return;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	protected static void printAgent(String string) {
		outClass.println(string);
	}
}

Pay attention to these points:

  1. comments - variable name inside ODF template
  2. metadata.addFieldAsTextStyling("comments",SyntaxKind.Html,false); - fill variable with something in HTML syntax
  3. <b>hello world</b> - HTML string to merge

NOTE: DominoAccess and JSFUtil are two very helpful classes written by Karsten Lehmann: see the full code. Thanks to them, DIIOP process for communication is not required.

Pro & Cons.

I think that it's a very powerful approach; the only limitation I've found is the restricted set of styles supported by the merge process (see there for details). Feel free to make some test using this approach, and let me know what you think :-)

read more

Apr 12, 2012

"Error cleaning up agent threads" - a never ending story

Filed Under:

A real case that has been solved

There are thousands of posts on Lotus Domino forum about this problem: "Error cleaning up agent threads".

I ran into a similar problem using a Java agent. I state that this agent has been inherited from another developer ;-) so it was not easy for me to initially understand what the problem could be.

In any case, every time the agent ended, I had the above error to the log.

In truth, none of the posts that I've seen has been helpful, until, by inspecting the code carefully and isolating the parts of it, I found the solution.

One line of code was:

Utils u = new Utils (..... some parameter ....);

 

where class was imported into the project.
Investigating the Script Libraries containing the class I discovered that it was defined as follows:
 
public class Utils extends AgentBase {

 

In practice, the class is defined to extend agentBase, so it's like it was an agent itself. The result is that each time it's loaded, a thread is istantiated.

When finished, the agent's thread was terminated, but the child's thread lost the reference to his father, causing the error.

This problem is naturally solved by changing the definition of the class:

public class Utils {

 

It may seem a trivial issue, but I guarantee that it is really sneaky, especially if you are working on other people's code.

As you can check on support forums, every developer has a different solution for this problem. In the end, my suggestion are:

- Never extend AgentBase in external class

- use recycle() methods to clean up memory object, expecially in loop code

Any other input on this issue is welcome.

Stay tuned!

Nov 04, 2011

Say hi to Android

Filed Under:

A brief introduction to Android for developers

In the last months, I have been looking for an opportunity to get into the Android world.

Finally, after a while I got the right one: porting an iPhone app to Android platform. 

The application is quite simple because it doesn't involve anything far from a common web application: it just retrieves information about events from a web service.

If you make a list of performed tasks, it would look like this:

  • retrieving the xml content
  • parsing the xml
  • storing a few configuration files
  • loading images
  • building a couple of data structures
  • showing everything using the specific views and widgets

 

Thanks to the reference documents and materials found in the Internet, I got a demo app smiling on my device in a couple of days (not a proper relase, but it could run smoothly).

If you are familiar with Eclipse, you will appreciate the effort made by Google, in order to provide a confortable developement environment. Let's see why.

From the official site you can download:

  • Android SDK (JDK required)
  • Android Development Tools (ADT) Plugin

 

You just need to set your SDK. And that's all! Ready to start. 

As you can easily guess, the default API provided by Google is Java-based. 

I know that some of you can argue "Java is not the best choice", but it's solid and well known by most programmers. It is also well documented and it comes with tons of libraries.

The Android application layout is done by a bunch of xml files. The ADT provides a nice interface to manage them.

All the Java code needed for your app can be taken from your personal toolset or grabbed from the net.

What I love most, is that you can easily connect your device and directly deploy the application on it. Moreover, for those who make use of remote debugging in Java, Android (DDMS) is faster and simpler then ever: just press the debug button and your code will be ready for debugging from your android device. Nothing new to the world, but as I said easier then I thought. 

A small disadvantage if you use the virtual device (AVD), because it is really slow, especially in loading.

Just a small consideration: because the hardware behind a mobile device is usually less powerful then a standard pc, you have to be careful.

In a few days the release of AppEventi for Android will be ready. http://www.ferraraterraeacqua.it/

Here is the iPhone app: http://itunes.apple.com/en/app/ferrara-eventi/id449817238?mt=8

Oct 28, 2011

Using JavaMail with TLS

Filed Under:

Snippets about sending mail though Java with TLS support

Here is an example to show you how to use JavaMail API method to send an email, using both TLS and SSL connection.

If you are using TLS, remember to disable this property:

props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

 

and enable
props.put("mail.smtp.starttls.enable","true")

 

These are the complete properties to set: 
props.put("mail.smtp.host", server);
props.put("mail.smtp.starttls.enable","true");
props.put("mail.smtp.auth", "true");
props.put("mail.debug","false");
props.put("mail.smtp.port",port);
props.put("mail.smtp.ssl.enable","true");
props.put("mail.smtp.socketFactory.port", port);
props.put("mail.smtp.socketFactory.fallback", "false");

 

Remember that, when you set Authentication to server with PasswordAuthentication class, you could experience this error:

"javax.mail.AuthenticationFailedException: 535-5.7.1 Username and Password not accepted."

Send by server if user or password used for authentication failed. Usually means that server requires full user name (user@server.com) to be used.

Jul 19, 2011

Problems with charset in 8.5.1 environment

Filed Under:

Strange behaviour managing XML file in Java agent

In this scenario, i've experienced some problems with UTF-8 charset in strings. In 6.5 environment everithing works fine.

This is my code in a Java agent:

org.w3c.dom.Document docXML = attachSegnatura.parseXML(false);
XSLTResultTarget out = new XSLTResultTarget();
out.setFileName(path + "SegnaturaRT.xml");
attachSegnatura.transformXML(xsl, out);
DOMParser parser = new DOMParser();
parser.parse(path + "SegnaturaRT.xml");
org.w3c.dom.Document docRT = ((DOMParser)parser).getDocument();

 

xsl is an EmbeddedObject that contains xsl file.
 
In this case, if XML contains characters like "§", these are wrongly rendered.
My solution was:
 
tFactory = TransformerFactory.newInstance();
StreamSource xslSS = new StreamSource(pathFile+"Segnatura.xsl");
StreamSource xslRT= new StreamSource(pathFile + "Segnatura.xml");
FileOutputStream fos = new FileOutputStream(pathFile + "SegnaturaRT.xml");
transformer = tFactory.newTransformer(xslSS);
transformer.transform(xslRT, new StreamResult(fos));
transformer.reset();
fos.close();
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
docRT = dBuilder.parse(new File(pathFile + "SegnaturaRT.xml"));

 

Anybody experienced something like this?