Saturday, December 29, 2012

Sunday, July 4, 2010

PDFViewer using ICEFaces, ICEPDF and Lucene

PDFViewer
My intention in this tutorial is to send some rays of light to some ICEFaces UIComponents such as: Collapsible panel, file upload, input/output text. Also we will know how to use effect tag component. Using ICEPdf and Lucene API we build searchable document and see how it's easy extract text from pdf and search through terms. Finally we wrap all our development into JSF web application.
Our application looks like that:
Let's begin.
Before delving into UIComponents explanation I would like to describe user's use case. Imagine a government worker who is not allowed install pdf reader by security reason. He tried explain his requirements to the IT team and here they are. He wants to upload pdf file into web server and read his stuff, also he wants to get possibility perform search on the uploaded file. IT team scratch their heads and understand that they can implement file uploading, but they don't know how to extract a text from pdf and how to provide searching capability. One very talented programmer from the team learned that ICESoft also has good and reliable package named ICEPDF that especially suites for that goal. This guy also suggests that if they will use Lucene API then search will be approachable.Well, it's time to transform the requirements to real JSF application. As we can see, at least two pages are needed, one for file upload and the second for viewer. The first question is how to navigate between pages? ICEFaces has a lot of navigational UI controls (tabs, collapsable or accordion and even more). My most likable ui control is Collapsible panel. Collapsible panel is a component consists of two parts: the content area and a header section, which can be clicked on, to cause the content area to collapse into not being visible, or expand to become visible. The code for file uploading looks like this:

<ice:panelCollapsible id="upload" expanded="true">
<f:facet name="header">
<ice:panelGroup>
<ice:outputText id="imageHeader" value="upload pdf files" />
ice:panelGroup>
f:facet>
<ice:panelGroup style="width: 100% ">
<ice:inputFile id="inputFileName"
autoUpload="#{inputFileController.autoUpload}"
actionListener="#{inputFileController.uploadFile}"
progressListener="#{inputFileController.fileUploadProgress}" />
<ice:outputProgress value="#{inputFileController.fileProgress}" />
<ice:inputHidden id="pathLucene" value="#{inputFileController.pathLuceneIndex}" />
<ice:inputHidden id="totalPages" value="#{inputFileController.totalPages}" />
ice:panelGroup>
ice:panelCollapsible>


After file is uploaded we need to extract the text provided by ICEPDF API:

PageText pageText = document.getPageViewText(pageNumber);
When text is extracted we build the Lucene's index. Think about pdf document, it contains pages, pages contain some text and text itself compound from words. If we project our pdf knowledge to Lucene's world, we get: document that wraps fields (pages), each field wraps terms (words). An index is a searchable ADT that works pretty well.
Here is a piece of code for building the index:

public void buildIndex(){
Document document = new Document();
String txt = null;
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
this.indexDirectory = getIndexPath();
IndexWriter writer;
try {
writer = new IndexWriter(FSDirectory.open(new File(getIndexDirectory())), analyzer, IndexWriter.MaxFieldLength.UNLIMITED);
/* Creates Lucene document */
for (int currentPage = 0; currentPage <>
try {
txt = getTe().getTextFromPage(currentPage);
document.add(new Field(String.valueOf(currentPage), txt, Field.Store.YES, Field.Index.ANALYZED));
} catch (Exception e) {
System.err.println(e.getLocalizedMessage());
}
}
writer.addDocument(document);
writer.optimize();
writer.close();
} catch (CorruptIndexException e1) {
e1.printStackTrace();
} catch (LockObtainFailedException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}


The second page – pdf viewer:

<ice:panelCollapsible id="pdfViewer" expanded="false">
<f:facet name="header">
<ice:panelGroup>
<ice:outputText id="textHeader" value="veiw pdf" />
ice:panelGroup>
f:facet>
<ice:panelGrid columns="8" style="width: 70%">
<ice:commandButton id="arrowDown"
image="./images/Arrowdowngreen.png"
actionListener="#{buttonsInputBean.imageButtonListener}" partialSubmit="true" />
<ice:commandButton id="arrowUp" image="./images/Arrowupgreen.png"
actionListener="#{buttonsInputBean.imageButtonListener}" partialSubmit="true" />
<ice:inputText id="currentPage"
value="#{buttonsInputBean.pageNumber}" partialSubmit="true"
valueChangeListener="#{buttonsInputBean.inputTextListener}">
<f:converter converterId="javax.faces.Integer" />
ice:inputText>
<ice:outputText id="separator" value="/"/>
<ice:inputText id="totalPage" value="#{buttonsInputBean.totalPages}" disabled="true" partialSubmit="true"/>
<ice:inputText id="search" value="#{buttonsInputBean.searchTerm}"
partialSubmit="true" effect="#{buttonsInputBean.effectOutputText}"
valueChangeListener="#{buttonsInputBean.inputTextListener}" />
<ice:commandButton id="searchRight"
image="./images/arightbl2_search.gif"
actionListener="#{buttonsInputBean.imageButtonListener}" partialSubmit="true" />
<ice:commandButton id="searchLeft"
image="./images/aleftbl4_search.gif"
actionListener="#{buttonsInputBean.imageButtonListener}" partialSubmit="true" />
ice:panelGrid>
<ice:panelGroup style="width: 50%; align: middle">
<ice:outputText id="textPage" style="align: center;" value="#{buttonsInputBean.regularText}" escape="false"/>
ice:panelGroup>
ice:panelCollapsible>


Since the text is extracted, an index built, we can try to search. By typing some word (query) and clicking on “Enter” key, a text in the inputText field colored Orange and the page with emphasized words appears. How do we achieve that? Simply like a charm. ICEfaces Component Suite provide special attributes that can be used to invoke effects on the components.


<ice:inputText id="search" value="#{buttonsInputBean.searchTerm}"
partialSubmit="true"
effect="#{buttonsInputBean.effectOutputText}"
valueChangeListener="#{buttonsInputBean.inputTextListener}" />


The Effect initialization is done by backing bean:

Highlight effectOutputText = new Highlight(WHITE_BKG);


That's it.
The source code of the PDFViewer located here:





Tuesday, May 5, 2009

Linkedin group profile

Hello, everybody.
I happy to announce that ICEFaces has its own Linkedin's group.

Join us and be part of the ICEfaces !

Monday, September 8, 2008

How to set up an Eclipse console for multilingual support


  1. In your code insert the following:

Locale loc = new Locale("ko", "KR");

Locale.setDefault(loc);

In this example I created an instance of Korean Locale, for the sake of example. Second line means, set Korean Locale as default. After those lines of code you can put your business logic.

To get the Eclipse console to display characters in an encoding other than your system default encoding, you need to do the following:

  1. Set the file encoding on the target VM in the Arguments Tab of the Run? dialog ("-Dfile.encoding=UTF-8").

  1. Set the console encoding to match the encoding you set on the target VM in the Common tab of the Run? dialog.

  • Open Run Dialog ...








  • Type "-Dfile.encoding=UTF-8" in the VM arguments















  • Select UTF-8 in the Common tab











That's it. Enjoy.

Tuesday, August 26, 2008

Some ICEFaces tags

First at all, why i've chosen an ICEFaces? There are some reasons and here they are:
1. It supports Ajax
2. It looks very cool
3. It works well
4. Integrated with Eclipse
5. It is possible to combine another frameworks like Spring, Struts and even more.

The first control i would rather speak about is ice:selectonelistbox
The control looks like this:





The corresponding listbox tag looks like this:
ice:selectOneListbox id="SlctFileListBoxID" title="files" size="5"
value="#{buttonclick.fileSelected}" partialSubmit="true"
style="width:100%;overflow: auto;"
valueChangeListener="#{buttonclick.fileChanged}">
f:selectItems id="SlctFilesItms"
value="#{buttonclick.filesListItems}" />
/ice:selectOneListbox>
I missed "<" sign because it's not correctly showing on the blog page How does it render on the html page?
<select class="iceSelOneLb" 
id
="j_id8:SlctFileListBoxID"
name
="j_id8:SlctFileListBoxID"
onblur="setFocus('');"
onchange
="iceSubmitPartial(form, this, event);"
onfocus
="setFocus(this.id);"
size="5"
style
="width:100%;overflow: auto;"
title="files">
<option value="allclasses-frame.html">allclasses-frame.htmloption>
...
select>

What we need in backing bean...
String fileSelected = "";
protected SelectItem[] FILES_ITEMS;

public SelectItem[] getFilesListItems() {
try {
FILES_ITEMS = new SelectItem[getFiles().length];
for (int i = 0; i < getFiles().length; i++) {
FILES_ITEMS[i] = new SelectItem(getFiles()[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
return FILES_ITEMS;
}

public void fileChanged(ValueChangeEvent event) {
if(event.getNewValue()!= null){
fileSelected = (String) event.getNewValue();
System.out.println("Clicked value: " + event.getNewValue());
}
}
public String getFileSelected() {
return fileSelected;
}

public void setFileSelected(String fileSelected) {
this.fileSelected = fileSelected;
}


That's it indeed.
But isn't actually. Something is incomplete...
And now time for theory.
The values—allclasses-frame.html ,Driver.html and so on.—are transmitted as request parameter values when a selection is made from the menu and the menu’s form is subsequently
submitted. Those values are also used as labels for the menu items. Sometimes you want to specify different values for request parameter values and item labels, so f:selectItems also has an itemLabel attribute:
f:selectItems itemValue="2008" itemLabel="Have a good day"/>

A single f:selectItems tag is usually better than multiple f:selectItem tags. If the number of items changes, you ought to adapt only Java code whether you use f:selectItems, whereas f:selectItem may entail to change both Java code and JSF pages.

That's all.

Sunday, May 18, 2008

Spring LDAP

When we're writing the intranet web application we should implement some authentification mechanism. There are a lot of ways to do it, but a more conveniently just to use a Spring LDAP framework. Here are the steps to set up this structure.
1. Download Spring LDAP form the site: http://springframework.org/ldap
2. Create Java Project;
2.1 Create package com.ldap.examples;
3. Import to it following jars:
i. commons-lang.jar
ii. commons-logging.jar
iii. commons-pool.jar
iv. ldapbp.jar
v. spring-beans.jar
vi. spring-context.jar
vii. spring-core.jar
viii. spring-dao.jar
ix. spring-jdbc.jar
x. spring-ldap-1.2.1.jar
4. Create PersonDao interface:

package com.ldap.examples;
import java.util.List;
public interface PersonDao {
public List getAllContactNames();
public List getContactDetails(String commonName,String lastName);
public List getPersonMail(String mail);
}

5. Create Java's class that implemets PersonDao interface


package com.ldap.examples;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
/**
* @param args
*/
public static void main(String[] args) {
Resource resource = new ClassPathResource("com/ldap/examples/springldap.xml");
BeanFactory factory = new XmlBeanFactory(resource);
LdapTemplate ldapContact = (LdapTemplate) factory.getBean("ldapTemplate");
PersonDaoImpl my = new PersonDaoImpl();
my.setLdapTemplate(ldapContact);
System.out.println(my.getPersonMail(desiredMail@company.com));
}
public List getAllContactNames() {
// TODO Auto-generated method stub
return null;
}
public List getPersonMail(String mail) {
AndFilter andFilter = new AndFilter();
andFilter.and(new EqualsFilter("objectclass","person"));
andFilter.and(new EqualsFilter("mail",mail));
System.out.println("LDAP Query " + andFilter.encode());
return ldapTemplate.search("", andFilter.encode(),new ContactAttributeMapper());
}
public List getContactDetails(String commonName, String lastName) {
// TODO Auto-generated method stub
return null;
}
}

6. Create springldap.xml
-??xml version="1.0" encoding="UTF-8"?>
-??!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" -"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
-??beans>
-?bean id="contextSource"
-?class="org.springframework.ldap.core.support.LdapContextSource">
-?property name="url" value="ldap://ldapServerIPorName:389" />
-?property name="base" value="o=company.com" />
-?/bean>
-?bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
-?constructor-arg ref="contextSource" />
-?/bean>
-?bean id="ldapContact"
-?class="com.ldap.examples.PersonDaoImpl">
-?property name="ldapTemplate" ref="ldapTemplate" />
-?/bean>
-?/beans>
Change ? to the < (Blogger bug???)
Put this file into package.
7. Create ContactDTO java's class

package com.ldap.examples;
public class ContactDTO {
String commonName;
String lastName;
String description;
String mail;
public String getCommonName() {
return commonName;
}
public void setCommonName(String commonName) {
this.commonName = commonName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLastName() {
System.out.println("Last name: " + lastName);
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String toString() {
StringBuffer contactDTOStr = new StringBuffer("Person=[");
contactDTOStr.append(" Mail = " + mail);
contactDTOStr.append(", Common Name = " + commonName);
contactDTOStr.append(", Last Name = " + lastName);
contactDTOStr.append(", Description = " + description);
contactDTOStr.append(" ]");
return contactDTOStr.toString();
}
public String getMail() {
System.out.println("Mail: " + mail);
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
}

8. Create ContactAttributeMapper


package com.ldap.examples;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import org.springframework.ldap.core.AttributesMapper;

public class ContactAttributeMapper implements AttributesMapper{
public Object mapFromAttributes(Attributes attributes) throws NamingException {
ContactDTO contactDTO = new ContactDTO();
String commonName = (String)attributes.get("cn").get();
if(commonName != null)
contactDTO.setCommonName(commonName);
String lastName = (String)attributes.get("sn").get();
if(lastName != null)
contactDTO.setLastName(lastName);
Attribute description = attributes.get("description");
if(description != null)
contactDTO.setDescription((String)description.get());
Attribute mail = attributes.get("mail");
if(mail != null){
contactDTO.setMail((String) mail.get());
}
return contactDTO;
}
}

That's all. Thanks to the Spring LDAP team!!!

Tuesday, March 25, 2008

How to integrate ICEfaces with WTP?

Ok. After almost a week of attempts to integrate ICEFaces v.1.6.2 with WTP (Eurora based on Eclipse 3.3) i still succeeded. The steps which have been described were not correct. The correct steps (at least for me) are:
Requirements:
1. As usual, Java 2 EE 5/6;
2. Eclipse IDE for Java EE Developers - Eurora 3.3.1
3. ICEFaces - ICEfaces-Eclipse-IDE-v3.0.0.zip and ICEfaces-1.6.2-libs-Eclipse.zip
4. Unzip all files from step 3.
5. In the Eclipse, go to Help -> Software Updates -> Find and Install
6. In the Features Updates check Search for new features to install
7. Click Next
8. In the Update sites to visit check Eurora site
9. Click Finish
10.Check all relevant updates and install
11. Repeat the step 5 - 7
12. Click New Local Site...
13. Select the directory where has been unzipped ICEFaces
14. Click OK
15. Select this location in the sites to include in search
16. Click Finish
17. Install the ICEfaces
So it's all.
Now you are able to use these faces into your project.