register
other register

Wednesday, December 10, 2008

Grails generate-views with all the fields

grails generate-views domainClass

By default, it will generate the list view with the first 6 fields including the primary key (i.e. id).

To tell the grails to generate the list view with all the fields, you can modify the file:

$GRAILS_HOME\src\grails\templates\scaffolding\lis.gsp

Then change the 6 in the "if (i < 6)" to a big number. There are two places to change, one is for the table column headers, the other is the row data.

Monday, August 18, 2008

Debugging GSP

When debugging GSP, you can use ?showSource at the end of the request url to show the comiled Java code to spot on the lines with erros.

Remote Debug Application on Tomcat

Monday, August 04, 2008

REST with Grails

Please refer to Grails Documentations for more info on REST with Grails. 

1. Create a grails project grails create-app rest 
2. Create a domain class with name Book 
create domain-class Book

class Book { 
String title 
String publisher 

3. Use generate-all to generate views and controller 

grails generate-all Book 

4. Modify Bootstrap.groovy to add sample data.
class BootStrap {      
 def init = { servletContext ->          
 new Book(title:"game1",publisher:"pub1").save()         
 new Book(title:"game2",publisher:"pub1").save()          
 new Book(title:"game3",publisher:"pub2").save()          
 new Book(title:"game4",publisher:"pub2").save()      
}      
def destroy = {      } }    

5. Modify UrlMappings.groovy  
 static mappings = {    
  "/book/$id?"(controller:"book"){        
   action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]    
  }  
 }  
6. Modify BookController.groovy show method
import grails.converters.* class BookController {  
 def show = {   
  if(params.id && Book.exists(params.id)) {    
   def b = Book.get(params.id)    
   render b as XML   
  }   
  else {    
   def all = Product.list()    
   render all as XML   
  }  
 }  
 .. 
}   
The method can be tested with this showTest.groovy client   
import org.apache.commons.httpclient.*; 
import org.apache.commons.httpclient.methods.*; 
def url = "http://localhost:8080/rest/book/${args[0]}" 
def method = new GetMethod(url)    
// Require HTTPClient library 
def client = new HttpClient() 
def statusCode = client.executeMethod(method) 
println "STATUS CODE:$statusCode" 
def stream = method.getResponseBodyAsStream() 
println "--- START RESPONSE BODY --- " 
System.out << stream println "n--- END RESPONSE BODY ---" 
stream.close() method.releaseConnection()  

Notes: You need to put Apache Commons Codec, and HTTPClient jars in the $GROOVY_HOMElib    

7. Modify BookController.groovy save method  
def save = {     
 def book = new Book(params['book'])     
 if(book.save())         
  render book as XML     
 else{         
  def myerrors = game.errors.allErrors.collect { 
   g.message(error:it) 
  }         
  render(contentType:"text/xml") {             
   errors {                 
    for(err in myerrors) {                     
     error(err)                 
    }             
   }         
  }     
 }         
}  

You can test the method with the client saveTest.groovy below:  

import org.apache.commons.httpclient.*; 
import org.apache.commons.httpclient.methods.*; 
def url = "http://localhost:8080/rest/game" 
def method = new PostMethod(url) 
def client = new HttpClient() 
def payload = """ <book>   
<title>newgame</title>   
<publisher>
newpublisher</publisher> 
</book> 
""" method.addRequestHeader("Content-Type","text/xml") method.addRequestHeader("Accept","text/xml,application/xml;q=0.9") method.setRequestEntity(new StringRequestEntity(payload)) def statusCode = client.executeMethod(method) println "STATUS CODE:$statusCode" def stream = method.getResponseBodyAsStream() println "--- START RESPONSE BODY --- " System.out << stream println "n--- END RESPONSE BODY ---" stream.close() method.releaseConnection() 

Tuesday, July 29, 2008

Groovlets

For more details please refer to: Groovlets


1. Put the following lines into web.xml in WEB-INF foler

=====================================

<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>

=====================================


2. Write your Groovlets hello.groovy like below, and put it into the webapps folder

=====================================

import java.util.Date
import groovy.xml.MarkupBuilder

if (session == null) {
session = request.getSession(true);
}

if (session.counter == null) {
session.counter = 1
}

html.html { // html is implicitly bound to new MarkupBuilder(out)
head {
title("Groovy Servlet")
}
body {
p("Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}")
}
}

session.counter = session.counter + 1

====================================

3. Copy groovy-all-xyz.jar into WEB-INF/lib folder


Notes:

If your Groovlets is at somewhere else rather than $CATALINA_HOME/webapps, then you need to edit the server.xml in the $CATALINA_HOME/conf like below:

<Context path="/groovy" docBase="c:/groovy-servlet"/>

Monday, July 28, 2008

Set System Properties in Java

You can set system properties by using -D in the java command line:
java -Dfile_properties=file.properties HelloWorld

==========================================

String file_properties = System.getProperty("file_properties");

// load file.properties file which sets the files and directories
Properties properties = new Properties();
try {
//properties.load(new FileInputStream("file.properties"));
properties.load(new FileInputStream(System.getProperty("file_properties")));
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1)
{
// TODO Auto-generated catch block e1.printStackTrace();
}

// get properties File inputDir = new File(properties.getProperty("inputDir"));

==========================================

In Eclipse, you can set system properties in:

1. Right click the java program, choose Run As --> Open Run Dialog
2. Select Arguments tab
3. In "VM argument", write your system properties settings. (i.e. -Dfile_properties=file.properties).

Thursday, July 17, 2008

Dealing with namespace in XSLT

For more general knowledge about namespace, please refer to XML Namespaces and How They Affect XPath and XSLT


If you have an vanilla xml file like below:

<people xmlns="http://xmlns.blabla.com/name" xmlns:abc="http://xmlns-abc.blabla.com/abc"
abc:firstname="jianfeng"
abc:lastname="tian" />

In your xslt, you have to define namespaces in your xsl:stylesheet declaration to make it working:

xmlns:jet="http://xmlns.blabla.com/name"
xmlns:abc="http://xmlns-abc.blabla.com/abc"

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jet="http://xmlns.blabla.com/name"
xmlns:abc="http://xmlns-abc.blabla.com/abc"
exclude-result-prefixes="abc xs xsi xsl">

<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

<xsl:template match="jet:people">
<person>

<xsl:attribute name="firstname">
<xsl:value-of select="@abc:firstname"/>
</xsl:attribute>

<xsl:attribute name="lastname">
<xsl:value-of select="@abc:lastname"/>
</xsl:attribute>

</person>

</xsl:template>
</xsl:stylesheet>

================================================================

If you have an xml file like below:

<people xmlns="http://www.somewhere.com/blabla" >
<person>
<address>
<postcode>110001</postcode>
</address>
<content group="firstname">John</person>
<content group="lastname">Smith</person>
</person>
</people>

In your xslt, you have to define namespaces in your xsl:stylesheet declaration to make it working:

xmlns:jet="http://www.somewhere.com/blabla"


<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jet="http://www.somewhere.com/blabla"
exclude-result-prefixes="jet xsl xsi">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="ISO-8859-1"/>

<xsl:template match="jet:people">
<data>
<postcode>
<xsl:value-of select="jet:person/jet:address/jet:postcode">
</postcode>

<firstname><xsl:value-of select="jet:person/jet:content[@group='firstname']" /></login>
<lastname><xsl:value-of select="jet:person/jet:content[@group='lastname']" /></login>

</data>
</xsl:template>

</xsl:stylesheet>

Friday, July 04, 2008

Convert CSV to XML Using XSLT

Refer to: http://blogs.msdn.com/kaevans/archive/2003/04/17/5780.aspx

In your CSV file,

1. Surround the whole data with tag.
2. Add tag to the first row which contains the heading titles
3. Add tag to contain the whole data.

<root>
<head>firstname, lastname</content>
<content>bla, bla
bla, bla</conent>
</root>

<xsl:stylesheet version ="2.0" xmlns:xsl ="http://www.w3.org/1999/XSL/Transform" > <xsl:output method ="xml"/> <!-- template that matches the root node--> <xsl:template match ="/" > <root> <xsl:call-template name ="texttorows" > <xsl:with-param name ="StringToTransform" select ="/root/content" /> <xsl:with-param name="heading" select="/root/heading" /> </xsl:call-template> </root> </xsl:template> <!-- template that actually does the conversion--> <xsl:template name ="texttorows" > <!-- import $StringToTransform--> <xsl:param name ="StringToTransform" select ="''" /> <xsl:param name="heading" select="''" /> <!-- <xsl:variable name="CR" select="'&#013;'"/> --> <xsl:variable name="CR" select="'&#xA;'"/> <xsl:choose> <!-- string contains linefeed--> <xsl:when test ="contains($StringToTransform,$CR)" > <!-- Get everything up to the first carriage return--> <row> <xsl:call-template name ="csvtoxml" > <xsl:with-param name ="StringToTransform" select ="substring-before($StringToTransform,$CR)" /> <xsl:with-param name="heading" select="$heading" /> </xsl:call-template> </row> <!-- repeat for the remainder of the original string--> <xsl:call-template name ="texttorows" > <xsl:with-param name ="StringToTransform" > <xsl:value-of select ="substring-after($StringToTransform,$CR)" /> </xsl:with-param> <xsl:with-param name="heading"> <xsl:value-of select="$heading" /> </xsl:with-param> </xsl:call-template> </xsl:when> <!-- string does not contain newline, so just output it--> <xsl:otherwise> <row> <xsl:call-template name ="csvtoxml" > <xsl:with-param name ="StringToTransform" select ="$StringToTransform" /> <xsl:with-param name="heading" select="$heading" /> </xsl:call-template> </row> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name ="csvtoxml" > <!-- import $StringToTransform--> <xsl:param name ="StringToTransform" select ="''" /> <xsl:param name ="heading" select ="''" /> <xsl:choose> <!-- string contains linefeed--> <xsl:when test ="contains($StringToTransform,',')" > <!-- Get everything up to the first carriage return--> <elem> <xsl:value-of select ="substring-before($heading,',')" /><xsl:text> </xsl:text> <xsl:choose> <xsl:when test="starts-with($StringToTransform, '&quot;')"> <xsl:value-of select ="substring-before($StringToTransform,'&quot;,')" /> </xsl:when> <xsl:otherwise> <xsl:value-of select ="substring-before($StringToTransform,',')" /> </xsl:otherwise> </xsl:choose> </elem> <!-- repeat for the remainder of the original string--> <xsl:call-template name ="csvtoxml" > <xsl:with-param name ="heading" > <xsl:value-of select ="substring-after($heading,',')" /> </xsl:with-param> <xsl:with-param name ="StringToTransform" > <xsl:choose> <xsl:when test="starts-with($StringToTransform, '&quot;')"> <xsl:value-of select ="substring-after($StringToTransform,'&quot;,')" /> </xsl:when> <xsl:otherwise> <xsl:value-of select ="substring-after($StringToTransform,',')" /> </xsl:otherwise> </xsl:choose> </xsl:with-param> </xsl:call-template> </xsl:when> <!-- string does not contain newline, so just output it--> <xsl:otherwise> <elem> <xsl:value-of select ="$heading" /><xsl:text> </xsl:text> <xsl:value-of select ="$StringToTransform" /> </elem> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>

Convert CSV to XML in Excel

Refer to: http://www.meadinkent.co.uk/xl_xml1.htm

The easiest way of converting the data in Excel to XML is to use some VB script

You can create an Excel Macro with the code below. Then in your excel workbook, run the macro.


Sub MakeXML()
' create an XML file from an Excel table
Dim MyRow As Integer, MyCol As Integer, Temp As String, YesNo As Variant, DefFolder As String
Dim XMLFileName As String, XMLRecSetName As String, MyLF As String, RTC1 As Integer
Dim RangeOne As String, RangeTwo As String, Tt As String, FldName(99) As String

MyLF = Chr(10) & Chr(13) ' a line feed command
DefFolder = "C:\" 'change this to the location of saved XML files

YesNo = MsgBox("This procedure requires the following data:" & MyLF _
& "1 A filename for the XML file" & MyLF _
& "2 A groupname for an XML record" & MyLF _
& "3 A cellrange containing fieldnames (col titles)" & MyLF _
& "4 A cellrange containing the data table" & MyLF _
& "Are you ready to proceed?", vbQuestion + vbYesNo, "MakeXML CiM")

If YesNo = vbNo Then
Debug.Print "User aborted with 'No'"
Exit Sub
End If

XMLFileName = FillSpaces(InputBox("1. Enter the name of the XML file:", "MakeXML CiM", "xl_xml_data"))
If Right(XMLFileName, 4) <> ".xml" Then
XMLFileName = XMLFileName & ".xml"
End If

XMLRecSetName = FillSpaces(InputBox("2. Enter an identifying name of a record:", "MakeXML CiM", "record"))

RangeOne = InputBox("3. Enter the range of cells containing the field names (or column titles):", "MakeXML CiM", "A3:D3")
If MyRng(RangeOne, 1) <> MyRng(RangeOne, 2) Then
MsgBox "Error: names must be on a single row" & MyLF & "Procedure STOPPED", vbOKOnly + vbCritical, "MakeXML CiM"
Exit Sub
End If
MyRow = MyRng(RangeOne, 1)
For MyCol = MyRng(RangeOne, 3) To MyRng(RangeOne, 4)
If Len(Cells(MyRow, MyCol).Value) = 0 Then
MsgBox "Error: names range contains blank cell" & MyLF & "Procedure STOPPED", vbOKOnly + vbCritical, "MakeXML CiM"
Exit Sub
End If
FldName(MyCol - MyRng(RangeOne, 3)) = FillSpaces(Cells(MyRow, MyCol).Value)
Next MyCol

RangeTwo = InputBox("4. Enter the range of cells containing the data table:", "MakeXML CiM", "A4:D8")
If MyRng(RangeOne, 4) - MyRng(RangeOne, 3) <> MyRng(RangeTwo, 4) - MyRng(RangeTwo, 3) Then
MsgBox "Error: number of field names <> data columns" & MyLF & "Procedure STOPPED", vbOKOnly + vbCritical, "MakeXML CiM"
Exit Sub
End If
RTC1 = MyRng(RangeTwo, 3)

If InStr(1, XMLFileName, ":\") = 0 Then
XMLFileName = DefFolder & XMLFileName
End If

Open XMLFileName For Output As #1
Print #1, ""
Print #1, ""

For MyRow = MyRng(RangeTwo, 1) To MyRng(RangeTwo, 2)
Print #1, "<" & XMLRecSetName & ">"
For MyCol = RTC1 To MyRng(RangeTwo, 4)
' the next line uses the FormChk function to format dates and numbers
Print #1, "<" & FldName(MyCol - RTC1) & ">" & RemoveAmpersands(FormChk(MyRow, MyCol)) & ""
' the next line does not apply any formatting
' Print #1, "<" & FldName(MyCol - RTC1) & ">" & RemoveAmpersands(Cells(MyRow, MyCol).Value) & ""
Next MyCol
Print #1, ""

Next MyRow
Print #1, "
"
Close #1
MsgBox XMLFileName & " created." & MyLF & "Process finished", vbOKOnly + vbInformation, "MakeXML CiM"
Debug.Print XMLFileName & " saved"
End Sub
Function MyRng(MyRangeAsText As String, MyItem As Integer) As Integer
' analyse a range, where MyItem represents 1=TR, 2=BR, 3=LHC, 4=RHC

Dim UserRange As Range
Set UserRange = Range(MyRangeAsText)
Select Case MyItem
Case 1
MyRng = UserRange.Row
Case 2
MyRng = UserRange.Row + UserRange.Rows.Count - 1
Case 3
MyRng = UserRange.Column
Case 4
MyRng = UserRange.Columns(UserRange.Columns.Count).Column
End Select
Exit Function

End Function
Function FillSpaces(AnyStr As String) As String
' remove any spaces and replace with underscore character
Dim MyPos As Integer
MyPos = InStr(1, AnyStr, " ")
Do While MyPos > 0
Mid(AnyStr, MyPos, 1) = "_"
MyPos = InStr(1, AnyStr, " ")
Loop
FillSpaces = LCase(AnyStr)
End Function

Function FormChk(RowNum As Integer, ColNum As Integer) As String
' formats numeric and date cell values to comma 000's and DD MMM YY
FormChk = Cells(RowNum, ColNum).Value
If IsNumeric(Cells(RowNum, ColNum).Value) Then
FormChk = Format(Cells(RowNum, ColNum).Value, "#,##0 ;(#,##0)")
End If
If IsDate(Cells(RowNum, ColNum).Value) Then
FormChk = Format(Cells(RowNum, ColNum).Value, "dd mmm yy")
End If
End Function

Function RemoveAmpersands(AnyStr As String) As String
Dim MyPos As Integer
' replace Ampersands (&) with plus symbols (+)

MyPos = InStr(1, AnyStr, "&")
Do While MyPos > 0
Mid(AnyStr, MyPos, 1) = "+"
MyPos = InStr(1, AnyStr, "&")
Loop
RemoveAmpersands = AnyStr
End Function

Thursday, July 03, 2008

Increase Java heap size

Refer to: http://hausheer.osola.com/docs/5

"
If Java runs out of memory, the following error occurs:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

This can have two reasons:

* Your Java application has a memory leak. There are tools like YourKit Java Profiler that help you to identify such leaks.
* Your Java application really needs a lot of memory (more than 128 MB by default!). In this case the Java heap size can be increased using the following runtime parameters:

java -Xms -Xmx

Defaults are:

java -Xms32m -Xmx128m

You can set this either in the Java Control Panel or on the command line, depending on the environment you run your application.

"

Wednesday, May 14, 2008

Write Logs to Different Files Using Log4J

If you have one generic java program to transform xml files from different directory.

java -Dfile_properties=/path/to/file.properties -jar YourProgramm.jar

You can specify different file.properties input directory using the "-D" parameter in the command line.

In each file.properties file, you specify:

input_dir=/bla/bla
xslt_dir=/bla/bla
output_dir=/bla/bla
log4j_properties=/bla/bla

Because you might want different logs for different transformations from different source, thus you can spot on which source file cause the problem.

By default, if you don't specify where to load the log4j configurations, it will load the log4j.properties, and then log4j.xml from the class folder, which would be uneditable when put in a jar. This might not what you want. You want to specify an external log4j configuration which you can edit outside of the program.

Here is what you can do:

Put the xml log4j file (e.g. xmllog4jconfig.xml) in the project directory.

In your java program

Public class Transformer {

private static Logger logger = Logger.getLogger(SaxonTransform.class);

public static void main(String[] args) {

DOMConfigurator.configure("xmllog4jconfig.xml");
logger.debug("Here is some DEBUG");
logger.info("Here is some INFO");
logger.warn("Here is some WARN");
logger.error("Here is some ERROR");
logger.fatal("Here is some FATAL");
}
}    


In the log4j xml configuration file, you can use LevelRange filter to log different levels of information into different log files like below:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>

<!-- log without a filter -->
<appender name="file"
class="org.apache.log4j.FileAppender">
<param name="File" value="logs/test.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
</layout>
</appender>

<!-- log with a filter -->
<appender name="debugfile"
class="org.apache.log4j.FileAppender">
<param name="File" value="logs/debug.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
</layout>

<!-- set the filter -->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="debug" />
<param name="LevelMax" value="debug" />
</filter>
</appender>

<root>
<priority value="debug"></priority>
<appender-ref ref="debugfile" />
<appender-ref ref="file" />
</root>
</log4j:configuration>


For more tutorials on Log4J, please have a look at:

Log4J Tutorial from The University Of Birmingham
Log4J Tutorial from Laliluna

Tuesday, May 06, 2008

Encoding in XSLT

If you have an xml document which contains German or French characters, then in your xml declarations, you should write:

<?xml version="1.0" encoding="ISO-8859-1"?>

Don't put "UTF-8" for it. It will not work

When using Java to write an xml fie, do the following:


File f = new File("newFile.xml");
FileOutputStream fos = new FileOutputStream(f);
OutputStreamWriter osw = new OutputStreamWriter(fos, Charset.forName("ISO-8859-1"));

// Begin to write an xml file.
osw.append("..");


Otherwise, if use the FileWriter instead of OutputStreamWriter, the programme will use the system default encoding which cause problems if the xml file contains German or French characters.

Remove unwanted namespace in XSLT

If we have the xslt like below:


<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="http://www.abc.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<content>
...
</content>

</xsl:stylesheet>


The result xml will be look like:


<conent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.polopoly.com/polopoly/cm/xmlio"
xmlns:abc="http://www.abc.com">
...
</conent>


The namespaces in the tag are not what we want. We can remove these namespaces by using

exclude-result-prefixes="xsl abc xsi" in the xsl:stylesheet declaration.

Monday, April 28, 2008

Escape <?xml version="1.0" encoding="UTF-8"?> in XML / XSLT

If an xml file is embeded in another xml file like below:

<?xml version="1.0" encoding="UTF-8"?>
..
<value>
<?xml version="1.0" encoding="UTF-8"?>
<name>Your name</name>
..
</value>
..

And you want to extract the embedded xml file and remove its xml declaration tag, then do the following:


<!-- Disable xml declaration by xslt -->
<xsl:output method="xml" omit-xml-declaration="yes" />

<!-- Define the string you want to remove -->
<xsl:variable name="declare">
<![CDATA[<?xml version="1.0" encoding="UTF-8"?>]]>
</xsl:variable>

<!-- Get the embedded xml file -->
<xsl:variable name="prepared">
<xsl:value-of select="value" />
</xsl:variable>

<!-- Remove the xml declaration and escape special characters -->
<xsl:value-of select="substring-after($prepared, $declare)"
disable-output-escaping="yes" />

Define variable using xsl:choose in XSLT


<xsl:variable name="doctype">
<xsl:choose>
<xsl:when test="condition1">value1</xsl:when>
<xsl:when test="condition2">value1</xsl:when>
<xsl:choose>
</xsl:variable>

Get filename using XSLT


<!-- Get the filename -->
<xsl:variable name="filename" select="tokenize(base-uri(.), '/')[last()]">

<!-- Split the filename using '\.' -->
<xsl:variable name="filenamepart" select="tokenize($filename, '\.')">

<!-- Remove the file extension -->
<xsl:value-of select="$filenamepart[1]"/>

Friday, April 11, 2008

How To Make A Web Site On The Local Machine Available To Other Computer Users If you deployed a web site on apache, tomcat, or IIS on port 80, then do the following: 1. Control Panel / Network Connections / Local Area Connection (right click) / Properties 2. Advanced tab / Windows Firewall (click Settings button) / Exception tab / click Add Port button 3. Enter Name (i.e. blackboard), Port number: 80 (TCP). 4. Then click "Change scope" button, select the relevant option.

Tuesday, April 08, 2008

How to Set JAVA_HOME in Linux


Set JAVA_HOME for One User

1. Make sure you are in bash mode by typing bash

2. Edit your .bashrc file vi ~/.bashrc

3. Add the following lines

JAVA_HOME=/usr/local/java/jdk1.5.0_15
export JAVA_HOME export PATH=$JAVA_HOME/bin:$PATH

Make sure put $JAVA_HOME/bin in front of $PATH, so that the system will look for javac and java first in the $JAVA_HOME/bin, and then $PATH.

4. Refresh the user profile using the following command.

source ~/.bashrc

Note: You have to make sure you are in the bash mode by typing: bash, otherwise the user profile won't be refreshed by the above source command.

5. Check your variables:

echo $JAVA_HOME
echo $PATH
which java
which javac


Set JAVA_HOME for All Users


1. Login as root.

2. Change it to bash shell by typing bash bash

3. Edit /etc/bashrc file vi /etc/bashrc

4. 5. 6 are the same as Set JAVA_Home for One User.

Saturday, January 26, 2008

Ant, Log4J, JUnit, Eclipse


1. Technical Specification


The target application environment Java compiler's version is Java 1.5.
Create a project in Eclipse.
Need to connect a database (e.g. Oracle).
Should log error message using Log4J.
Delivery the project as a jar.


2. Procedures


2.1 Create User Library for Log4J and JDBC driver (oracle-jdbc)

2.2 Add these two user libraries to the project build path

2.3 Establish The Application Environment in Eclipse

2.3.1 Create file.properties file as a property file for the application to read

2.3.2 Create a log4j.properties file for log4j to read

2.4 Follow the instructions to set up JUnit available to Ant

2.5 Create build.xml file



2.1 Create User Libraries


1. In Eclipse, Window > Preferences > Java > Build Path > User Libraries.
2. Click New button, and enter the 'user library name'.
3. Click Add JARs... button, and select the jar files to be added to this user library.
4. Click OK button.

Do the above steps for Log4J, and oracle-jdbc


2.2 Add User Libraries to the project


1. Right click the project, and select properties (or Alt+Enter).
2. Select Java Build Path > Libraries, and click Add Library... button
3. Select User Library, and click next> button
4. Select lo4j, and oracle-jdbc (the two we just created in the previous step).
5. Click OK to add these user libraries to the project environment.

Doing this can make sure these libraries are in the project classpath.



2.3 Establish Project Structure

1. Create build directory, and its sub directories (classes and jar)
md build/classes
md build/jar
2. Create lib directory, and copy log4j and oracle-jdbc jars in.

2.3.1 Create file.properties file as a property file for the application to read
name1 = value1
name2 = value2

2.3.2 In the src directory, create a log4j.properties file for log4j to read. Below is a sample

For more info about log4j, please look at: How to use log4j logging API and Log4J Manual

************************************************
# Log levels
# Uncomment the following line to enable full loggin for every class
#log4j.rootLogger=trace, stdout, R
log4j.logger.pma=error, stdout, R

# Console appender configuration
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

# Rolling File Appender
log4j.appender.R=org.apache.log4j.RollingFileAppender
# Path and file name to store the log file.
log4j.appender.R.File=./logs/error.log
log4j.appender.R.MaxFileSize=500KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
# Rolling File Appender layout
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d - %c - %p - %m%n

****************************************************


2.4 Follow the instructions to set up JUnit available to Ant

2.5 Create build.xml file

For more information about Ant and its build.xml file, please have a look at Tutorial: Hello World with Ant

Below is a sample build.xml file:

********************************************************

<?xml version="1.0"?>

<project name="pma" basedir="." default="main">
<property name="src.dir" value="src">
</property>
<property name="build.dir" value="build">
</property>
<property name="classes.dir" value="${build.dir}/classes">
</property>
<property name="jar.dir" value="${build.dir}/jar">
</property>
<property name="main-class" value="pma.PMADAO_Split">
</property>
<property name="lib.dir" value="lib">
</property>
<property name="report.dir" value="${build.dir}/junitreport">
</property>

<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar">
</fileset>
</path>

<target name="clean">
<delete dir="${build.dir}">
</delete>
</target>

<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}"
classpathref="classpath"
source="1.5"
target="1.5"
compiler="javac1.5"/>
<copy todir="${classes.dir}">
<fileset dir="${src.dir}" excludes="**/*.java">
</fileset>
</copy>
</target>

<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}" update="true">

<zipfileset src="${lib.dir}/ojdbc14.jar"/>
<zipfileset src="${lib.dir}/log4j-1.2.15.jar"/>

<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>

<target name="run" depends="jar">
<java fork="true" classname="${main-class}">
<classpath>
<path refid="classpath">
</path>
<path id="application" location="${jar.dir}/${ant.project.name}.jar">
</path>
</classpath>
</java>
</target>

<target name="clean-build" depends="clean,jar">
</target>
<target name="main" depends="clean,run">
</target>

<target name="junit" depends="jar">
<mkdir dir="${report.dir}" />
<junit printsummary="yes">
<classpath>
<path refid="classpath" />
<path refid="application" />
</classpath>
<formatter type="xml" />
<batchtest fork="yes" todir="${report.dir}">
<fileset dir="${src.dir}" includes="*Test.java" />
</batchtest>
</junit>
</target>

<target name="junitreport">
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}" includes="TEST-*.xml"/>
<report todir="${report.dir}"/>
</junitreport>
</target>

</project>

**********************************************




In the task "jar", the following code can make sure the created jar (pma.jar) file contains the following libraries (log4j, and oracle-jdbc jars) in an extracted format. If the libraries (log4j, and oracle-jdbc jars) in the created jar (pma.jar) are in its jar format, then the application won't find the libraries in its classpath when running.

<zipfileset src="${lib.dir}/ojdbc14.jar"/>
<zipfileset src="${lib.dir}/log4j-1.2.15.jar"/>


In the task "compile", we have to add:

source="1.5" target="1.5" compiler="javac1.5" to make the application java 1.5 compatible on the target environment, otherwise it will complains about "class version something" when running.








Tuesday, January 15, 2008

crontab

Weblog Analysis with Webalizer

Webalizer is a web log analysis tool which runs very fast. You can download Webalizer Here.

By default

>./webalizer logfile

will process only one log file. If all the logs are in one file, then it is ok. But for logs which rotate on a daily basis, you have to configure webalizer to do so.

Here is my webalizer.conf, which should be at the webalizer command directory.

**********************************************
...
LogType clf
OutputDir /outputdirectory
HistoryName webalizer.hist
Incremental yes
IncrementalName webalizer.current
...
**********************************************


Create a script foo.sh like below will do the business:

**********************************************
#!/bin/sh

for file in /logfiles/*
do
/pathtowebalizer/webalizer $file
done
exit 0
**********************************************

Friday, January 11, 2008

Java Logging using java.util.logging

Java's logging facility (see Sun's overview and API ) has two parts : a configuration file, and an API for using logging services. It is a good tool, and is perfectly fine for simple and moderate logging needs. (For complex logging needs, you might consider the log4j tool as an alternative.)

Log entries can be sent to these destinations, as either simple text or as XML :

  • the console
  • a file
  • a stream
  • memory
  • a TCP socket on a remote host
The Level class defines seven levels of logging enlightenment :
  • FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE
  • ALL and OFF are defined values as well
Here is one style of using these Levels in code, which may be modified as desired :
  • upon startup, use CONFIG to log configuration parameters
  • during normal operation, use INFO to log high-level "heartbeat" information
  • when bugs or critical conditions occur, use SEVERE
  • debugging information might default to FINE, with FINER and FINEST used occasionally, according to taste.
There is flexibility in how logging levels can be changed at runtime, without the need for a restart :
  • simply change the configuration file and call LogManager.readConfiguration.
  • or, change the level in the body of your code, using the logging API ; for example, one might automatically increase the logging level in response to unexpected events
Levels are attached to these items :
  • an originating logging request (from a single line of code)
  • a Logger (usually attached to the package containing the above line of code)
  • a Handler (attached to an application)
The flow of execution for a particular logging request usually proceeds as follows :

logging request of some level is made to logger attached to current package
if the request level is too low for that package's logger {
discard it
}
otherwise {
cycle through all handlers {
if the request level is too low for that handler {
discard it
}
otherwise {
log the request
}
}
}

Here is an example of a logging configuration file :

# Properties file which configures the operation of the JDK
# logging facility.

# The system will look for this config file, first using
# a System property specified at startup:
#
# >java -Djava.util.logging.config.file=myLoggingConfigFilePath
#
# If this property is not specified, then the config file is
# retrieved from its default location at:
#
# JDK_HOME/jre/lib/logging.properties

# Global logging properties.
# ------------------------------------------
# The set of handlers to be loaded upon startup.
# Comma-separated list of class names.
# (? LogManager docs say no comma here, but JDK example has comma.)
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# Loggers and Handlers may override this level
.level=INFO

# Loggers
# ------------------------------------------
# Loggers are usually attached to packages.
# Here, the level for each package is specified.
# The global level is used by default, so levels
# specified here simply act as an override.
myapp.ui.level=ALL
myapp.business.level=CONFIG
myapp.data.level=SEVERE

# Handlers
# -----------------------------------------

# --- ConsoleHandler ---
# Override of global logging level
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

# --- FileHandler ---
# Override of global logging level
java.util.logging.FileHandler.level=ALL

# Naming style for the output file:
# (The output file is placed in the directory
# defined by the "user.home" System property.)
java.util.logging.FileHandler.pattern=%h/java%u.log

# Limiting size of output file in bytes:
java.util.logging.FileHandler.limit=50000

# Number of output files to cycle through, by appending an
# integer to the base file name:
java.util.logging.FileHandler.count=1

# Style of output (Simple or XML):
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter

An application should likely centralize the naming policy for Loggers, since switching naming styles becomes a simple edit in one method, instead of a large number of edits spread throughout the application. Reasons to alter the naming policy might include :

  • in a shared environment, adding an application name to the Logger name can help distinguish one application's entries from others
  • adding application version information might be beneficial for debugging
There is a common complaint with the JDK logging config file : a Handler cannot be restricted to a specific Logger. That is, Handlers defined in the config file are global, in the sense that they will be attached to all Loggers. This is particularly annoying in a server environment, since different applications will, by default, output logging statements into the same file. This behavior can be easily changed, but only by using the JDK logging API. Many would prefer to do this directly in the config file.

Here is an example of using the logging API :

package myapp.business;

import java.util.logging.*;

/**
* Demonstrate Java's logging facilities, in conjunction
* with a logging config file.
*/

public final class SimpleLogger {

public static void main(String argv[]) {
SimpleLogger thing = new SimpleLogger();
thing.doSomething();
}

public void doSomething() {
//Log messages, one for each level
//The actual logging output depends on the configured
//level for this package. Calls to "inapplicable"
//messages are inexpensive.
fLogger.finest("this is finest");
fLogger.finer("this is finer");
fLogger.fine("this is fine");
fLogger.config("this is config");
fLogger.info("this is info");
fLogger.warning("this is a warning");
fLogger.severe("this is severe");

//In the above style, the name of the class and
//method which has generated a message is placed
//in the output on a best-efforts basis only.
//To ensure that this information is always
//included, use the following "precise log"
//style instead :
fLogger.logp(Level.INFO, this.getClass().toString(), "doSomething", "blah");

//For the very common task of logging exceptions, there is a
//method which takes a Throwable :
Throwable ex = new IllegalArgumentException("Some exception text");
fLogger.log(Level.SEVERE, "Some message", ex);

//There are convenience methods for exiting and
//entering a method, which are at Level.FINER :
fLogger.exiting(this.getClass().toString(), "doSomething");

//Display user.home directory, if desired.
//(This is the directory where the log files are generated.)
//System.out.println("user.home dir: " + System.getProperty("user.home") );
}

// PRIVATE //

//This logger will inherit the config of its parent, and add
//any further config as an override. A simple style is to use
//all config of the parent except, perhaps, for logging level.

//This style uses a hard-coded literal and should likely be avoided:
//private static final Logger fLogger = Logger.getLogger("myapp.business");

//This style has no hard-coded literals, but forces the logger
//to be non-static.
//private final Logger fLogger=Logger.getLogger(this.getClass().getPackage().getName());

//This style uses a static field, but hard-codes a class literal.
//This is probably acceptable.
private static final Logger fLogger =
Logger.getLogger(SimpleLogger.class.getPackage().getName());
}


Example Run 1 , using the config file defined above :

>java -Djava.util.logging.config.file=C:\Temp\logging.properties
-cp . myapp.business.SimpleLogger

ConsoleHandler is configured to show only SEVERE messages :

Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

While the FileHandler shows ALL that is sent to it :

Jan 8, 2003 10:43:46 AM myapp.business.SimpleLogger doSomething
CONFIG: this is config
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
INFO: this is info
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
WARNING: this is a warning
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:43:47 AM class myapp.business.SimpleLogger doSomething
INFO: blah
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

Example Run 2, showing the ConsoleHandler output, using the logging.properties file which ships with the JDK, and which logs only INFO level and above :

>java -cp . myapp.business.SimpleLogger

Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
INFO: this is info
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
WARNING: this is a warning
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:52:31 AM class myapp.business.SimpleLogger doSomething
INFO: blah
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
at myapp.business.SimpleLogger.main(SimpleLogger.java:13)