Monday, December 29, 2008

Deploying Web Service in Apache AXIS2

The purpose of this article is to talk about how to write web service and deploy it in Apache AXIS2.

First step is downloading and installing apache axis2. There are two ways to use Apache AXIS2.

1. Install AXIS2 as a standalone server using the Standard Binary Distribution.
2. Deploy axis2.war and deploy it in a servlet container.

We will be using the 2nd method in the article. To check the installation open the url http://localhost:8080/axis2 and you should be able to see the home page of apache axis2.

Please locate the folder axis2\WEB-INF\services in exploded war file of axis2. This is the folder where you will copy the code of your service and it will get deployed in apache axis2.

Lets us take a simple example. Write your service class.

public class HelloWorld
{
public String sayHello(String firstName, String lastName)
{
System.out.println("Hello World Invoked");
MessageContext incomingContext = MessageContext.getCurrentMessageContext();
FileDataSource dataSource = new FileDataSource("c:/YServer.txt");
DataHandler dataHandler = new DataHandler(dataSource);
incomingContext.addAttachment("contentID", dataHandler);

String data = DataProvider.getData();
return "Hello " + firstName + " " + lastName + " " + data;
}
public String getServiceName()
{
return "test service";
}
}

Write a services.xml file.

<service>
<description>
This is my first service, which says hello
</description>
<parameter name="ServiceClass">HelloWorld</parameter>
<operation name="sayHello">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<operation name="getServiceName">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
</service>

In case you want to receive the SOAP payload as XML Message in the service you need to specify XML Message receiver instead of RPCMessageReceiver in services.xml file. To specify XML Message receiver put the following line as child of <operation> tag in your services.xml.

<messageReceiver

class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>

Also, since the service will receive XML Message as input parameter you need to change the signature of the method of service.

public OMElement sayHello(OMElement element)

OMElement object contains the XML Message, OMElement Object is provided by AXIOM. AXIOM stands for AXis Object Model (also known as OM - Object Model) and refers to the XML infoset model that was initially developed for Apache Axis2.


Bundle your class file, services.xml in the hierarchy as shown in the figure below. See helloworld folder in image below.



Copy your service into the services folder of axis2 as shown in figure, start web server. Open the URL http://localhost:8080/axis2.

On the displayed page Click on services, you should be able to see the name of service you specified in services.xml in the list of web services.

Monday, December 1, 2008

Getting Started with Apache Lucene

Apache Lucene is a high-performance, full-featured text search engine library.

The API of lucene is very simple to use. Here is overview of some of the objects required to start using Apache Lucene.

Document and Fields

The class "org.apache.lucene.document.Document" is necessary container for the index. Lucene requires all indexed objects to provide an instance of Document. Each document defines one or several fields ( (org.apache.lucene.document.Field). Fields contain classified information about the document or metadata related to document. A sample classification is for example the creation date of a file, author of the document etc. These fields allow you to search later for a specific information in this classification.


IndexWriter

The class org.apache.lucene.index.IndexWriter creates the index. Via the method addDocument you can add an existing Document to the index. The constructor for IndexWriter expects the directory to store the index, and the analyzer for the content of the files. In addition a boolean flag is handed over which indicates if the index should be created new or if an existing index should be extended.

Analyser

The class org.apache.lucene.analysis.standard.StandardAnalyzer provides a standard analyzer. This part is responsible to analyse the text and to filter out certain fill-words, e.g. "and".

Searcher and Query

The class org.apache.lucene.search.Searcher provides the search functionality. org.apache.lucene.search.IndexSearcher searches over an index. What is to be searched is provided via the query (org.apache.lucene.search.Query) class. The search allows wildcard search, e.g. *, ?, logical operations (AND, OR, NOT) and much more, e.g. fussy.

Here's a simple example how to use Lucene for indexing and searching.

public class TestMyLucene
{
public static void main(String[] args) throws Exception
{
Analyzer analyzer = new StandardAnalyzer();

// Store the index in memory:
Directory directory = new RAMDirectory();

// To store an index on disk, use this instead (note that the
// parameter true will overwrite the index in that directory
// if one exists):
//Directory directory = FSDirectory.getDirectory("/tmp/testindex", true);
IndexWriter iwriter = new IndexWriter(directory, analyzer, true);
iwriter.setMaxFieldLength(25000);

Document doc = new Document();
String text = "This is the text to be indexed.";
doc.add(new Field("fieldname", text, Field.Store.YES, Field.Index.TOKENIZED));
iwriter.addDocument(doc);
iwriter.close();

// Now search the index:
IndexSearcher isearcher = new IndexSearcher(directory);

// Parse a simple query that searches for "text":
Query query = QueryParser.parse("text", "fieldname", analyzer);
Hits hits = isearcher.search(query);
System.out.println(hits.length());

// Iterate through the results:
for (int i = 0; i < hits.length(); i++)
{
Document hitDoc = hits.doc(i);
System.out.println(hitDoc.get("fieldname"));
}

isearcher.close();
directory.close();
}
}

In the above example, hits return the matching documents for the query provided.

Friday, November 28, 2008

Calling Struts Action by Javascript

To call a struts action by Javascript on an event, you need to write a function which is invoked when the event occurs and this function makes an AJAX call. The AJAX call can send a request to the same URL which is provided in action attribute of form tag or html:form tag.

e.g. In case your action URL is something like
"/registerUserAction.do?action=forgetPasswordForward"
that you provide in action attribute of html:form, you can pass the same url to the mentioned code snippet below:


function makeRequest(url, callbackfunction)
{
var http_request = getXMLHttpObject();
url = url + "isComingFromAjax=true";

if (url.indexOf('?') != -1)
{
url = url + "&" ;
}
else
{
url = url + "?" ;
}

url = url + "isComingFromAjax=true";
http_request.onreadystatechange = callbackfunction;
http_request.open('POST', url.substring(0, url.indexOf('?')), true);
http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
http_request.send(url.substring(url.indexOf('?') + 1));
}

Some of points to be considered in given code snippet:
1. callbackfunction is the function which will be invoked when response is received
2. "isComingFromAjax=true" parameter has been added as a extra parameter to differentiate between the normal action request and the AJAX request to do any special handling on the server side.

I have not covered details of creating XMLHttpRequest Object and other AJAX related things assuming you know it. You can write the logic for handling the response received from server in callbackfunction.

Wednesday, November 26, 2008

Oracle - Rank Function

Rank calculates the rank of a value in a group of values. Rows with equal values for the ranking criteria receive the same rank. Oracle then adds the number of tied rows to the tied rank to calculate the next rank. Therefore, the ranks may not be consecutive numbers.

Consider the following Schema

CREATE TABLE USER_TEST
(
user_id numeric(12),
first_name varchar2(32),
last_name varchar2(32),
age numeric(3),
salary numeric(7)
);

insert into user_test values(1, 'Pardeep', 'Kumar', 26, 20000);
insert into user_test values(2, 'Raj', 'Sharma', 23, 15000);
insert into user_test values(3, 'Jai', 'Singh', 30, 30000);
insert into user_test values(4, 'Rana', 'Pratap', 32, 35000);
insert into user_test values(5, 'Nakul', 'Gupta', 23, 16000);
insert into user_test values(6, 'Ritu', 'Kumar', 22, 10000);
insert into user_test values(7, 'Sita', 'Dikshit', 27, 22000);
insert into user_test values(8, 'Gurpal', 'Bhatia', 38, 49000);
insert into user_test values(9, 'Bhim', 'Kumar', 23, 19000);
insert into user_test values(10, 'Lokesh', 'Shriram', 22, 11500);

Aggregation

As an aggregate function, RANK calculates the rank of a hypothetical row identified by the arguments of the function with respect to a given sort specification.

The constant argument expressions and the expressions provided in the ORDER BY clause of the aggregate match by position. Therefore, the number of arguments must be the same and their types must be compatible.

Single Column RANK as Aggregation Function

Syntax:

RANK () WITHIN GROUP (ORDER BY NULLS )

Example:
The following query returns the rank for a user with salary 10,000SELECT RANK(20000) WITHIN GROUP
(ORDER BY salary DESC NULLS LAST) SAL_RANK
FROM user_test;

Result: SAL_RANK = 5

Multiple Column RANK as Aggregation Function

RANK () WITHIN GROUP (ORDER BY NULLS )

The following query returns the rank of a user a salary of 15,000 and age 23 years.

SELECT RANK(23, 15000) WITHIN GROUP
(ORDER BY age desc, salary desc) RANK
FROM user_test;

Result: Rank = 8


Analytic

As an analytic function, RANK computes the rank of each row returned from a query with respect to the other rows returned by the query, based on the values of the value_exprs in the order_by_clause.

RANK Analytic Function

RANK () OVER ()

Find the user with the 2nd highest salarySELECT * FROM (SELECT user_id, first_name, last_name, salary, RANK() OVER (ORDER BY salary DESC) userrank
FROM user_test) WHERE userrank = 2;

Result:

USER_ID FIRST_NAME LAST_NAME SALARY USERRANK
4 Rana Pratap 35000 2 RANK() OVER ( )

Arrange table by age and then salary

SELECT user_id,first_name, last_name, age, salary,
RANK() OVER (PARTITION BY age ORDER BY salary DESC) RANK
FROM user_test;

Result:

USER_ID FIRST_NAME LAST_NAME AGE SALARY RANK
10 Lokesh Shriram 22 11500 1
6 Ritu Kumar 22 10000 2
9 Bhim Kumar 23 19000 1
5 Nakul Gupta 23 16000 2
2 Raj Sharma 23 15000 3
1 Pardeep Kumar 26 20000 1
7 Sita Dikshit 27 22000 1
3 Jai Singh 30 30000 1
4 Rana Pratap 32 35000 1
8 Gurpal Bhatia 38 49000 1


Return all users with age 23 ordered by salary

SELECT user_id,first_name, last_name, age, salary,
RANK() OVER (PARTITION BY age ORDER BY salary DESC) RANK
FROM user_test where age = 23;

USER_ID FIRST_NAME LAST_NAME AGE SALARY RANK
9 Bhim Kumar 23 19000 1
5 Nakul Gupta 23 16000 2
2 Raj Sharma 23 15000 3

Although the examples quoted here may looks like that we are paying more cost for the same things that can be done by simple order by, but simple examples have been chosen to make it easy to understand how to use RANK.

Generating Image Thumbnails

When I started working on this initially, I started looking into open source tools for this, after spending couple of hours on open source tools I suddenly jumped into oracle site and was pleased to see a good support for image processing in oracle. Oracle provides support to create thumbnail of images stored as blob. Here is simplest way to create image thumbnails. Create a table that will store the original image and the thumbnail image content as blob.

CREATE TABLE test_thumbnail
(
id NUMERIC(12) PRIMARY KEY,
image_content BLOB,
thumbnail_content BLOB
);

Create a stored procedure that will update the thumbnail_content field after image is stored in the database.

create or replace PROCEDURE IMAGE_THUMB_PROCEDURE
(imgId IN NUMBER , imgAttribute IN varchar2) AS
imageId integer := 0; verb varchar2(100);
src_blob BLOB;
dst_blob BLOB;
BEGIN imageId := imgId; verb := imgAttribute;
update test_thumbnail set thumbnail_content=empty_blob() where id = imageId;
select image_content into src_blob from test_thumbnail where id = imageId;
select thumbnail_content into dst_blob from test_thumbnail where id = imageId for update; ordsys.ordimage.processCopy(src_blob ,verb ,dst_blob);
update test_thumbnail set thumbnail_content = dst_blob where id = imageId;
END IMAGE_THUMB_PROCEDURE;

Invoke the stored procedure passing the appropriate imgAttribute, passing different kind of image attributes you can process the original image content, for scaling down the image to a thumbnail of 100x100 pixels, pass the following parameter:

“maxscale=100 100 fileformat=jfif”

Maxscale is the size of the generated image and fileformat has its obvious meaning. The method ordsys.ordimage.processCopy(src_blob ,verb ,dst_blob) copy a source blob into a destination blob doing the processing as per attributes passed as verb.

For more details on oracle media solution support please read the following article: http://www.oracle.com/technology/sample_code/products/intermedia/index.html

Once the stored procedure is invoked image thumbnail is stored as blob in field thumbnail_content.

Difference between Delete, Drop and Truncate Statements

DELETE, DROP and TRUNCATE statements differ in their nature itself.
a. DELETE statement is a DML (DATA MANIPULATION LANGUAGE) statement.
b. DROP AND TRUNCATE statements are DDL (DATA definition language) statements.

A DML action can be rolled back if the data is not committed, but DDL actions cannot be rolled back until Oracle 9i. With Oracle 9i FLASH BACK enabled even some of the DDL statements can be rolled back.

Delete

A delete statement deletes the data from a table. A delete statement can have a “where” clause, which need to be satisfied for the data to be deleted from the table.

Actions of Delete statement


  • Deletes the data from the current table space.
  • Modifies the undo table space with the delete records.
  • Executes all the before / after statement and row level triggers.
  • Updates the indexes (makes the index empty if the where clause is omitted).
  • Constraint checks are performed before deleting the rows
Syntax of a delete Statement:
DELETE FROM table [WHERE condition]

Truncate

Truncate drops all the records in a table. But as it is a DDL statement data cannot be retrieved. Usually truncate is faster than delete statement because there is no need to change or update the UNDO table space with the deleted records. Truncate is an implicit commit Statement. Truncate statement deal locates the space.

Actions of Truncate Statement
a. Removes all the records from the current table space.
b. Updates the indexes.
c. High watermark of the truncated table is reset.
d. Integrity Constraint checks are performed

Syntax of a Truncate Statement:
TRUNCATE TABLE table_name;

DROP STATEMENT

A drop statement removes the table object from the database. Structure and the data will be removed from the database. Action cannot be rolled back (but only by FLASH BACK from Oracle 9i).

Actions of a Drop Statement:
a. Constraint checks are performed before dropping.
b. All the data and structure of the table will be removed.
c. Updates the corresponding data dictionary views like dba_objects, dba_tables, user_tables e.t.c
d. Updates the Status as “INVALID” for all the dependent objects.
e. Deallocates the Space

Syntax of a Drop Statement:
DROP TABLE table_name;
DROP TABLE table_name cascade constraints;

If “cascade constraints” is specified all foreign keys that reference the table are dropped and then table is dropped.