Sunday, July 25, 2010

Locate Images in and out From Your Eclipse RCP application

Loading images and other resources are a little different in Eclipse RCP than the normal Java applications. In this post, I'm going to list all the possible ways to locate images in and out of your Eclipse RCP applications.

To simplify the example code, I only show you how to get ImageDescriptor. After that, you can use createImage() to crate Image object from

1. Icons Folder Inside RCP project



Activator.getImageDescriptor("icons/tray.png");

2. from class path

If images are saved in the same place as your class:


ImageDescriptor.createFromFile(
    BookCoverView.class, "cover.jpg");


3. from external disk or Internet

This is helpful when you ask user to specify a image from their disk.

ImageDescriptor.createFromURL(
new URL("file:d://icons/info2.gif"));

4. reuse Eclipse's icons


Many common images (undo, redo, file, folder, cut, copy) are available thru the org.eclipse.ui.ISharedImages interface. You can get them with IWorkbench:



PlatformUI.getWorkbench().getSharedImages()
.getImage(ISharedImages.IMG_OBJ_FOLDER);

source: http://blog.cypal-solutions.com/2008/02/eclipse-icons.html


5. Access icons from other plugin


access the icons from other plugins without bundling them in your plugin:

AbstractUIPlugin.imageDescriptorFromPlugin(
"org.eclipse.ui", "$nl$/icons/import_wiz.gif");


source: http://blog.cypal-solutions.com/2008/02/eclipse-icons-follow-up-post.html







Wednesday, July 21, 2010

Generate Book Cover from Pdf Documents using PdfBox

PdfBox is a very powerful tool to deal with Pdf documents. Today I will show you how to generate image from pdf page and re-size it to use as book cover. The book cover usually has two images, one for thumbnail, 180*236, the other is large one, 500*656, as you see from some book store websites.



There is a "PdfImageWriter" class shipped with PdfBox. However, this class is not flexible. It can only generate a series of image with fixed size and auto-increased file name(such as image1.jpg, image2.jpg). Therefore, I create a "FlexiablePdfImageWriter" class to generate and write image with defined weight and height.

public class FlexiablePdfImageWriter  {

 private PDDocument document;
 // default setting for image params
 private int resolution = 96;
 private int imageType = BufferedImage.TYPE_INT_RGB;
 private String imageFormat = "jpg";

 public FlexiablePdfImageWriter(PDDocument document) {
  this.document = document;
 }

/**
 * Generate image for given page number, begin at 1
 * @param pageNo page number
 * @return image
 * @throws IOException
 */
 public BufferedImage getOriginalImageForSinglePage(int pageNo)
   throws IOException {
  List pages = document.getDocumentCatalog().getAllPages();

  PDPage page = (PDPage) pages.get(pageNo - 1);
  BufferedImage image = page.convertToImage(imageType, resolution);

  return image;

 }

 /**
  * resize image with width, height
  * @param orginalImage
  * @param width
  * @param height
  * @return resiced image
  */
 public BufferedImage resizeImage(BufferedImage orginalImage, int width, int height)
   {

  BufferedImage resizedImage = new BufferedImage(width, height, imageType);
  resizedImage.getGraphics().drawImage(orginalImage, 0, 0, width, height, null);

  return resizedImage;

 }
 /**
  * resize image by percentage
  * @param orginalImage
  * @param percentage
  * @return scaled image
  */
 public BufferedImage scaleImage(BufferedImage orginalImage, int percentage)
{
  int width= orginalImage.getWidth()*percentage/100;
  int height= orginalImage.getHeight()*percentage/100;

  return this.resizeImage(orginalImage, width, height);

}
 /**
  * write image to file
  * @param image
  * @param path
  * @return
  * @throws IOException
  */
 public boolean writeImagte(BufferedImage image, String path)
   throws IOException {
  File outfile = new File(path);
  ImageIO.write(image, imageFormat, outfile);
  return true;
 }
 

 public int getResolution() {
  return resolution;
 }

 public void setResolution(int resolution) {
  this.resolution = resolution;
 }

 public int getImageType() {
  return imageType;
 }

 public void setImageType(int imageType) {
  this.imageType = imageType;
 }

 public String getImageFormat() {
  return imageFormat;
 }

 public void setImageFormat(String imageFormat) {
  this.imageFormat = imageFormat;
 }

}


How to use FlexiablePdfImageWriter:

@Test
 public void testGenerateImage() throws Exception {
  
  //prepare Pdf document
  File file = new File("Mastering Regular Expressions.pdf");
  PDDocument document = PDDocument.load(file);
  
  
  FlexiablePdfImageWriter imageWriter = new FlexiablePdfImageWriter(document);

    //you can change the default setting  uses setter
  //imageWriter.setImageFormat("png");
  //imageWriter.setResolution(64);

  //orginal size, PdfBox calculated by it's page size, margin and others.
  BufferedImage orginalImage = imageWriter.getOriginalImageForSinglePage(1);
  
  //generate thumbnail, size:180*236
  BufferedImage thumbnail = imageWriter.resizeImage(orginalImage, 180, 236);
  
  imageWriter.writeImagte(thumbnail, "thumbnail.jpg");
  
  //generate large cover, size:500*656
  BufferedImage large = imageWriter.resizeImage(orginalImage, 500, 656);
  
  imageWriter.writeImagte(large, "large.jpg");

//scale the orginal image, make it half size,  50% 
  BufferedImage half = imageWriter.scaleImage(orginalImage, 50);
  
  imageWriter.writeImagte(large, "half.jpg");
 }

Thursday, July 15, 2010

Java XML Persistence with XStream

Database is usually used to persist data as it provides flexible query, high performance and other powerful functions. In Java, there are JDBC, in the low level operation with SQL and Java Persistence API aiming object level solution. But they are too heavy when we only need simple data persistence without complicated query requirement. In this scenario, XML seems to be the best choice, plus XStream, provides a light weight and convenience persistence alternative. In this post, I will show you how easy to use XStream implement basic CRUD operation against XML storage.



The Model


I define a Identiable interface to get Id(primary key) from model. The id can be String, Long or Integer.
public interface Identiable {
 public Object getId();
}

I will use Book(isbn is the primary key) as example:
import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("book")
public class Book implements Identiable {

 @XStreamAlias("isbn")
 private String isbn;

 @XStreamAlias("booktitle")
 private String title;

 @XStreamAlias("description")
 private String description;

 .... Getter and Setter....
      
 @Override
 public Object getId() {
  return this.getIsbn();
 }

I use annotation @XStreamAlias("isbn") to format the xml elment name. Here is what the serialized xml looks like:


  1847199747
  Spring Security 3
  
SECURE YOUR WEB APPLICATIONS AGAINST MALICIOUS INTRUDERS WITH THIS EASY TO FOLLOW PRACTICAL GUIDE 



The XMLPersistence Basic

XmlPersistence implements the basic CRUD operation.

import java.io.File;
import java.util.Collection;

import com.thoughtworks.xstream.persistence.FilePersistenceStrategy;
import com.thoughtworks.xstream.persistence.PersistenceStrategy;
import com.thoughtworks.xstream.persistence.XmlMap;

public class XmlPersistence {

 private XmlMap xmlMap;

 /**
  * 
  * @param dir where xml files persist
  */
 public XmlPersistence(String dir) {
  PersistenceStrategy strategy = new FilePersistenceStrategy(
    new File(dir));
  xmlMap = new XmlMap(strategy);
 }

 public void save(Identiable entity) {

  xmlMap.put(entity.getId(), entity);
 }

 public Identiable retrive(Identiable entity) {
  return (Identiable) xmlMap.get(entity.getId());
 }

 public void update(Identiable entity) {

  xmlMap.put(entity.getId(), entity);
 }

 public void delete(Identiable entity) {

  xmlMap.remove(entity.getId());
 }

 @SuppressWarnings("unchecked")
 public Collection getAll() {
  return xmlMap.values();
 }

 

}

How to use


//save xml files in "xml" directory
XmlPersistence xmlDao = new XmlPersistence("xml");

//Create
xmlDao.save(this.getBook1());
xmlDao.save(this.getBook2());


//Reterive
Book book = (Book) xmlDao.retrive(getBook1());

//Update
Book book1 = this.getBook1();
book1.setDescription(book1.getDescription().toUpperCase());

xmlDao.update(book1);

//Delete
xmlDao.delete(this.getBook1());
//Iterate 
Collection all = xmlDao.getAll();
  
  for (Identiable entity : all) {
   Book book = (Book) entity;

   System.out.println(book);

 }


//test data
private Book getBook1() {

  Book book = new Book();

  book.setTitle("Spring Security 3");
  book.setIsbn("1847199747");
  book.setDescription("Secure your web applications against malicious intruders with this easy to follow practical guide ");
  return book;
 }

 private Book getBook2() {

  Book book = new Book();

  book.setTitle("Lucene in Action");
  book.setIsbn("1933988177");
  book.setDescription("with clear writing, reusable examples, and unmatched advice on best practices, Lucene in Action, Second Edition is still the definitive guide to developing with Lucene.");
  return book;
 }

XStream will generated xml file for each object:


XStream is only 401K and it only depends on xpp3, just 24.3K.

Wednesday, July 14, 2010

Extract ISBN Number From PDF Using JAVA



Part 1: What is ISBN?

ISBN's are described on the ISBN

The International Standard Book Number is known throughout the world as a short, clear and potentially machine-readable identification number which marks any book unmistakably

A valid ISBN should:(define in wikipeda)
The ISBN is 13 digits long if assigned after January 1, 2007, and 10 digits long if assigned before 2007. An International Standard Book Number consists of 4 or 5 parts:

1. for a 13 digit ISBN, a GS1 prefix: 978 or 979 (indicating the industry; in this case, 978 denotes book publishing)
2. the group identifier, (language-sharing country group)
3. the publisher code,
4. the item number, (title of the book) and
5. a checksum character or check digit.

The ISBN separates its parts (group, publisher, title and check digit) with either a hyphen or a space. Other than the check digit, no part of the ISBN will have a fixed number of digits.


Here are some examples:

Part 2: Extract Plain Text From PDF


There are several open source Pdf libraries available in Java world. PdfBox is the one choosen for extracting text from pdf document in this case.

Instead of loading the whole document into memory, We will do extracting page one by one since most of ebooks list their ISBN within the first 10 page.

//we will only try to extract within 10 pages
final int MAX_PAGE = 10;
//begin at the first page
int start = 1;


boolean found = false;
//create PDDocument via Java.io.File
PDDocument document = PDDocument.load(file);
PDFTextStripper stripper = new PDFTextStripper();

  while (!found && start <= MAX_PAGE) {

   stripper.setStartPage(start);
   stripper.setEndPage(start + 1);

   String pageText = stripper.getText(document);

   if (pageText!= null) {
  //You can do something about the extracted text here 

                //if done, mark found=true, finish extracting.
    found = true;
   } else {
    start++;
   }

  }
  if (document != null) {
   document.close();
  }

Part 3: Extract ISBN from Plain Text using Regular Expression


Extracted Plain Text Example

Eclipse Rich Client Platform Second Edition

First printing, May 2010 (in Page 5)
Copyright © 2010 Pearson Education, Inc.
All rights reserved. Printed in the United States of America. This publication is protected by copyright,
and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a
retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying,
recording, or likewise. For information regarding permissions, write to:
Pearson Education, Inc.
Rights and Contracts Department
501 Boylston Street, Suite 900
Boston, MA 02116
Fax: (617) 671-3447
ISBN-13: 978-0-321-60378-4
ISBN-10: 0-321-60378-8
Text printed in the United States on recycled paper at RR Donnelley in Crawfordsville, Indiana.
First printing, May 2010

Lucene in Action Second edition (in Page 5)
ISBN 978-1-933988-17-7
Printed in the United States of America
1 2 3 4 5 6 7 8 9 10 – MAL – 15 14 13 12 11 10

Step 1: extract every line with text "ISBN"
Regular Expression "^.*ISBN.*$" will match the whole line contains string "ISBN".
public final String isbnLinePattern = "^.*ISBN.*$";

        //line contains ISBN information should be 4+10 chars at least
        public final int MIN_LENGTH = 14;

 public List extractISBNLines(String content) {
 Pattern pattern = Pattern.compile(isbnLinePattern, 
        Pattern.MULTILINE| Pattern.CASE_INSENSITIVE);
  Matcher lineMatcher = pattern.matcher(content);
  boolean result = lineMatcher.find();
 List list = new ArrayList();
        // Loop through and create a new String 
        // with the replacements
        while(result) {
         String line = lineMatcher.group();
if (line.length() >= MIN_LENGTH) {

     list.add(line);
    }
            result = lineMatcher.find();
        }

  }
  return list;

 }
After this extraction, we will get two lists for the two books in this examples:

Eclipse Rich Client Platform Second Edition
ISBN-13: 978-0-321-60378-4
ISBN-10: 0-321-60378-8
Lucene in Action Second edition
ISBN 978-1-933988-17-7

Step 2: extract ISBN-10 or ISBN-13 line by line
After step 1, we get clear ISBN line so the extraction is much easier now. So the regular expressions I used here is weak.

public final String isbn10Pattern = "[-0-9Xx ]{13}";

 public final String isbn13Pattern = "[-0-9Xx ]{17}";

public Book extractISBN(List list, Book book) {

  for (String line : list) {
   if (book.getIsbn13() == null) {
 String isbn13 =extractByPattern(this.isbn13Pattern, line);
    book.setIsbn13(isbn13);
   }
   if (book.getIsbn10() == null) {
 String isbn10 = extractByPattern(this.isbn10Pattern, line);
    book.setIsbn10(isbn10);
   }
  }

  return book;
 }

 /**
  * Extract information by regular expression
  * 
  * @param patternStr
  * @param content
  * @return matched string, null if not find
  */
 public static String extractByPattern(String patternStr, String content) {

  Pattern pattern = Pattern.compile(patternStr);
  Matcher matcher = pattern.matcher(content);
  if (matcher.find())
   return matcher.group(0);
  else
   return null;
 }

That's it, enjoy extracting!

Sunday, July 11, 2010

Generate SHA1 File Checksum Using HTML5 File API in Javascript

JavaScript was not allow to read a file in local computer for such a long time. However, as HTML5 came out, reading files in Javascript became possible. In this post, I will demonstrate how to implement dragging and dropping a file into browser and generating its SHA1 checksum. There are three parts in this post, drag and drop handling, file reading via HTML5 File API and SHA1 JavaScript implementation.

Currently, only Firefox 3.6+ and Chrome 6+ support HTML5 File API.



DEMO


1. Drag and Drop

What we need to do is in ondrop function when user drops a file in browser. In this function, we need to transfer the dropped object as "FileList" object.

var holder = document.getElementById('holder');
holder.ondragover = function () { 
 this.className = 'hover'; 
 setHolderContent("");
 return false; 
 };

holder.ondragend = function () { 

 this.className = '';
 setHolderContent("");
  return false; 

  };

holder.ondrop = function (e) {
   e.preventDefault();

  var file = e.dataTransfer.files[0];

  processFile(file);

}


2. Reading file with HTML5 File API

The events we are interested is during reading (onprogress) and when reading finish (onloadend). And we read file content as birnary string.
function processFile(file){
 
    var reader = new FileReader();
    //update precentage while file reading 
    reader.onprogress = function (evt) {
    if (evt.lengthComputable) {
      var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
      if (percentLoaded <=100) {
       updateProgess(percentLoaded);
        }
    }
  }
    //reading finish, do sha1 calcuate.
    reader.onloadend = function (event) {
   
         
  var result = sha1Stream(event.target.result); 
   var precentIndicator = document.getElementById('holder');
    setHolderContent(result);
    updateProgess(100);
    
  };



reader.readAsBinaryString(file);
 
}

3. SHA1 JavaScript implementation

There are several JavaScript implementation available, so I just borrowed: Paul Johnston at http://pajhome.org.uk/crypt/md5/contrib/sha1_stream.js Jason Davies's stream version: http://pajhome.org.uk/crypt/md5/contrib/sha1_stream.js
function sha1Stream(input){
   
     var blocksize = 512;
     var h = naked_sha1_head();
     for (var i = 0; i < input.length; i += blocksize) {
         var len = Math.min(blocksize, input.length - i);
        var block = input.substr(i, len);
        naked_sha1(str2binb(block), len*chrsz, h);
        
     }
     var result = binb2hex(naked_sha1_tail(h));
     return result;
}
I have to thank sunetos, who help me with the stream version.

Due JavaScript's performance issues, my small SHA1 generator cannot handle large file(I suggest you try file under 2M). And It seems Chrome 6 handle large file better than firefox 3.6.



Sunday, June 27, 2010

apache.commons.dbcp.BasicDataSource Losts Connection with MySql after Long Time Inactive

I deployed my webapp into could server last week. Of course, I'm the only user of this webapp and just use it once a day currently. But when I use it, it reports the same exception every day:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin failed

After I scroll down the Exception, the root cause is:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was49672 seconds ago.The last packet sent successfully to the server was 49672 seconds ago, which is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.

To solve this problem, the best way is to add property "testOnBorrow" in the dataSource configuration in you applicationContext.xml :

true


By adding testOnBorrow dbcp parameter, it will force a sanity check on every connection before it's used.

Friday, June 18, 2010

Linux for Java Programmer - Maven and Subversion

This part will setup the build environment, using subversion to check out source code and building it with Maven.

1.Install Maven2


sudo apt-get install maven2


2. Install Subversion


sudo apt-get install subversion

3. SVN check out

svn checkout http://your-repo.com/project/trunk projectname

4. Build with Maven

mvn clean package

Now you can copy your app.war to tomcat's webapps' directory.