Sunday, December 25, 2011

How to access Carbon data-source from a web-application

WSO2 Application Server shift with inbuilt Carbon data-source feature. This post describes how to create a Carbon data-source using WSO2 Application Server and access it within a web-application.

Step1 : Create sample database

create database wes_db;
use wes_db;
create table wes_engineer(WES_ID integer, WES_NAME varchar(30));
insert into wes_engineer values(1, 'Amy');
insert into wes_engineer values(2, 'Alfred');

Step2 : Create carbon data-source

To be able to refer Carbon data-source within a web-app, first you need to register your Carbon data-source with a JNDI naming service. When you creating a carbon data-source there is option to select data-source repository type. From there select JNDI as repository and provide the jndi_context_class and the provider_url. You can use "com.sun.jndi.rmi.registry.RegistryContextFactory" JNDI service provider inside Carbon server.



      
                                  









Image 01: Adding Carbon data-source



Step3 : Create a simple web-app

This simple web app will access the above data-source and print all data available in wes_engineer table.

Once you registered your carbon data-source with a JNDI service, you can access that data-source directly within your web application by doing a JNDI lookup. Following code segment will illustrate you how to call carbon data-source within web-app.

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); //same context factory class provided when creating carbon data-source.
env.put(Context.PROVIDER_URL, "rmi://localhost:2195"); //same provider url provided when creating carbon data-source.

InitialContext ctx = new InitialContext(env);
DataSource ds = (DataSource) ctx.lookup("jndiDS"); //"jndiDS" is the carbon data-source name that i have created. Please note that you may need to add commons-dbcp.jar and commons-pool.jar to web-app class path to do this cast.

conn = ds.getConnection();

The full TestCarbonDataSource.java  servlet class and project web.xml files are provided below.

TestCarbonDataSource.java
package org.wso2;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.naming.Context;
import javax.sql.DataSource;
import javax.naming.InitialContext;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.*;


public class TestCarbonDataSource extends HttpServlet {

public TestCarbonDataSource() {
super();
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
performTask(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
performTask(request, response);
}

private void performTask(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<br/>");
out.println(testJndiDataSource());
}

public String testJndiDataSource() {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
StringBuffer sb = new StringBuffer();
try {

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://localhost:2195");
InitialContext ctx = new InitialContext(env);

DataSource ds = (DataSource) ctx.lookup("jndiDS");
conn = ds.getConnection();

st = conn.createStatement();
rs = st.executeQuery("SELECT * FROM wes_engineer");

while (rs.next()) {
String id = rs.getString("WES_ID");
String firstName = rs.getString("WES_NAME");
sb.append("ID: " + id + ", First Name: " + firstName + "
");
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); }
try { if (st != null) st.close(); } catch (SQLException e) { e.printStackTrace(); }
try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
return sb.toString();
}
}
web.xml

<web-app>
    <servlet>
        <servlet-name>TestCarbonDataSource</servlet-name>
        <servlet-class>org.wso2.TestCarbonDataSource</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestCarbonDataSource</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Once you create web-app, you can host it in WSO2 Application Server or any other application servers.


Image 02: Deploy web-app in Application server.

Once web-app is deployed in Application server click on "Go To URL" link or directly go to the link using browser "http://localhost:9763/carbonDSwebApp/"

  
Image 03: Accessing web-app

You can see the output as bellow.

                               

Saturday, August 27, 2011

How to call a Oracle Stored Function from WSO2 Data Service?

1. Create table executing the following command.

CREATE TABLE TEAMS(id INTEGER, team VARCHAR(30));

2. Following is the stored function which returns total number of entries in the above table.

CREATE OR REPLACE FUNCTION myfunction(ename IN VARCHAR, eid IN NUMBER) RETURN INTEGER 
AS myCount INTEGER;
BEGIN
    INSERT INTO TEAMS values(eid, ename);
    SELECT COUNT(*) into myCount from TEAMS;
    RETURN myCount;
END;

/

3.  Create the data service using following content.

<data name="testOracleFunction">
   <config id="or">
      <property name="org.wso2.ws.dataservice.driver">oracle.jdbc.driver.OracleDriver</property>
      <property name="org.wso2.ws.dataservice.protocol">jdbc:oracle:thin:user/pwd@localhost:1521/XE</property>
      <property name="org.wso2.ws.dataservice.user">user</property>
      <property name="org.wso2.ws.dataservice.password">pwd</property>
   </config>

   <query id="q1" useConfig="or">
      <sql>{call ?:=myfunction(?,?)}</sql>
      <result element="TotalTeams" rowName="">
         <element name="totalTeams" column="totalTeams" xsdType="xs:integer" />
      </result>
      <param name="totalTeams" sqlType="INTEGER" type="OUT" ordinal="1" />
      <param name="ename" sqlType="STRING" ordinal="2" />
      <param name="eid" sqlType="INTEGER" ordinal="3" />
   </query>

   <operation name="op1">
      <call-query href="q1">
         <with-param name="ename" query-param="ename" />
         <with-param name="eid" query-param="eid" />
      </call-query>
   </operation>

</data>

Note the sql query used to call the stored function: "{call ?:=myfunction(?,?)}". First input parameter carries the return value of function. Other two parameters are inputs to the function.
Important thing is we need to define a Input parameter with OUT type to get the result of function(i.e the first parameter in above sql query). Then we need to define a Output parameter to get this value as a result set from data service. Following section in the data service is used to do this.

<result element="TotalTeams" rowName="">
         <element name="totalTeams" column="totalTeams" xsdType="xs:integer" />
</result>
<param name="totalTeams" sqlType="INTEGER" type="OUT" ordinal="1" />
     

Monday, August 15, 2011

ORA-14551: cannot perform a DML operation inside a query

While I was calling a oracle function which initially insert data into a table, I got an error "ORA-14551: cannot perform a DML operation inside a query". I thought to share this post since it can be help to anyone who having the same error.

 Bellow is the oracle function:

CREATE OR REPLACE FUNCTION myfunction(ename IN VARCHAR) RETURN INTEGER AS eid INTEGER;
BEGIN
    INSERT INTO TEAM values(5,ename);
    SELECT id into eid from TEAM WHERE TEAM.name=ename;
    RETURN eid;
END;
/
And I called the function using the command,
SQL> select myfunction('test') from dual; 
which cause to give the above error.


Found that we cannot use 'select' to call functions which perform DML(insert. delete, update) operations.  It should be called using the 'call' command as bellow.

SQL> var myvar NUMBER;
SQL> call myfunction('test') into :myvar;  

Call completed.

SQL> print myvar;
MYVAR
----------
     5