Pages

Friday, May 30, 2008

Using Siebel WS in your ADF application

For our Siebel Unit I was making a demo which can search siebel accounts, displays a single account with the addresses and an insert page. The goal was to do this with the Siebel web service. This example gives an good example how to deal with complex web services, because the ADF Web Service Datacontrol only works with simple web services. You have a example on otn which use the ws datacontrol, but this only works on a simple web service method. When you want to search or create a new account you have to do more.
This is how it looks. The search page show all accounts ( calls SiebelAccountQueryByExample ). In this example you can search on Id and Name. You can also click on the Id then the account detail page is loaded.
When the detail page is loaded it calls SiebelAccountQueryById with the Id of the search page.

And the last page is the insert page

I did this by making a web service proxy on the local saved wsdl. To test this web service you have to do a lot.

nl.ordina.siebel.model.proxy.DefaultClient myPort = new nl.ordina.siebel.model.proxy.DefaultClient();
System.out.println("calling " + myPort.getEndpoint());
// Add your own code here
ListOfAccountInterfaceTopElmt inter = new ListOfAccountInterfaceTopElmt();
Account[] result = new Account[1];
Account acc = new Account();
acc.setAccountId("1-7SL");;
result[0]= acc;
inter.setListOfAccountInterface(result);
ListOfAccountInterfaceTopElmtHolder holder = new ListOfAccountInterfaceTopElmtHolder(inter);
myPort.siebelAccountQueryByExample(holder);
Account[] accs = holder.value.getListOfAccountInterface();
acc = accs[0];

This is to complex so I made it a bit easier. I created a java class where I call these ws methods and on this class I am making a datacontrol.

package nl.ordina.siebel;

import nl.ordina.siebel.model.proxy.DefaultClient;
import nl.ordina.siebel.model.proxy.holders.ListOfAccountInterfaceTopElmtHolder;
import nl.ordina.siebel.model.proxy.types.com.siebel.xml.account_interface.Account;
import nl.ordina.siebel.model.proxy.types.com.siebel.xml.account_interface.ListOfAccountInterfaceTopElmt;

public class SiebelActions {
public SiebelActions() {
}

public Account[] SearchAccount( Account msg ){
try {
DefaultClient myPort = new DefaultClient();
System.out.println("calling " + myPort.getEndpoint());
ListOfAccountInterfaceTopElmt inter = new ListOfAccountInterfaceTopElmt();
Account[] result = new Account[1];
result[0]= msg;
inter.setListOfAccountInterface(result);
ListOfAccountInterfaceTopElmtHolder holder = new ListOfAccountInterfaceTopElmtHolder(inter);
myPort.siebelAccountQueryByExample(holder);
return holder.value.getListOfAccountInterface();
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

public Account[] GetAccount( String id ){
try {
DefaultClient myPort = new DefaultClient();
System.out.println("calling " + myPort.getEndpoint());
ListOfAccountInterfaceTopElmt result = myPort.siebelAccountQueryById(id);
return result.getListOfAccountInterface();
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

public static void main(String[] args) {

SiebelActions sa = new SiebelActions();
Account acc = new Account();
acc.setAccountId("1-7SL");
Account[] result = sa.SearchAccount(acc);
acc = result[0];
System.out.println("account info "+acc.getAlias()+" name "+acc.getName());

result = sa.GetAccount("1-7SL");
acc = result[0];
System.out.println("account info 2 "+acc.getAlias()+" name 2 "+acc.getName());
}
}

I created an ADF Datacontrol on this class and use this datacontrol in the ADF JSF Pages. Because the input parameters of the ws methods are complex types, I had to create a bean with a method which returns the input parameter. In this bean I also added the search parameters field.
getAccount is used in the pagedef as input parameter on SiebelAccountQueryByExample.

package nl.ordina.siebel;

import nl.ordina.siebel.model.proxy.types.com.siebel.xml.account_interface.Account;

public class SiebelBean {

protected java.lang.String accountId;
protected java.lang.String showAccountId;
protected java.lang.String alias;
protected java.lang.String name;

public SiebelBean() {
}

public Account getAccount(){
Account acc = new Account();
acc.setAccountId(accountId);
acc.setAlias(alias);
acc.setName(name);
return acc;
}

public void setAccountId(String accountId) {
this.accountId = accountId;
}

public String getAccountId() {
return accountId;
}

public void setShowAccountId(String showAccountId) {
this.showAccountId = showAccountId;
}

public String getShowAccountId() {
return showAccountId;
}

public void setAlias(String alias) {
this.alias = alias;
}

public String getAlias() {
return alias;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

Here is a guide with all the steps I did. And here is the jdeveloper 10.1.3.3 project. To let it work on your own siebel 8 you only have to change the url of the ENDPOINT_ADDRESS_PROPERTY in the stub file created by the web service proxy and put in the right username / password else you can't insert new accounts.
_setProperty(ENDPOINT_ADDRESS_PROPERTY, "http://10.100.17.1/eai_nld/start.swe?SWEExtSource=WebService&SWEExtCmd=Execute&UserName=normani&Password=normani");

Sunday, May 25, 2008

Using OpenLDAP for net8 and AQ connection factory

In this blog I will show you can use OpenLDAP to lookup your Oracle Net (tnsnames) connections or AQ connection factories. Now you don't need to have an Oracle Internet Directory (OID) installed. The first step is to download openldap. Install this and download my oracle.schema and put this in the schema folder of openldap. This file has to be included in the slapd.conf.
Edit slapd.conf and add
"include schema/java.schema"
"include schema/oracle.schema" .
Make sure anonymous can read the tnsnames entries.
access to dn="" by * read
access to *
by self write
by users read
by anonymous read
We have to configure the ldap for Oracle by making an OracleContext and oracledbconnections entry.

I do this with ldapbrowser. You can download it here. For this free java application I made some templates which you can use.



Do the same for oracledbconnections ( this is for the AQ entries) Now use the queue template in ldapbrowser.

To make a tnsnames entry I select the OracleContext ldap entry and I use the orclNetService template to create a new Oracle connection.

We are ready to use it, we only has to create ldap.ora in the network\admin folder. The ldap.ora looks like this.
DIRECTORY_SERVERS= (localhost:389:636)

DEFAULT_ADMIN_CONTEXT = "o=sgi,c=us"

DIRECTORY_SERVER_TYPE = OID

We start sqlplus and use the ldap name as tnsnames entry. I can also start net manager and go the Directory menuitem to see our ldap entry

The second part shows you can lookup an AQ connection factory with JNDI and ldap. first let's register a new connection in the ldap server. For this I only need the following jars files. jndi-1.2.1.jar,jta-1.0.1.jar, jms-1.1.jar,aqapi.jar and the jdbc jar of oracle



package jms2;

import java.util.Hashtable;
import java.util.Properties;
import javax.jms.JMSException;
import javax.naming.Context;
import oracle.jms.AQjmsFactory;

public class register_jms_to_oid {
public register_jms_to_oid() {
Hashtable env = new Hashtable(5, 0.75f);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389");
env.put("server_dn", "o=sgi,c=us");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "o=sgi,c=us");
env.put(Context.SECURITY_CREDENTIALS, "secret");
String url = "jdbc:oracle:thin:@XPCND7010XMP:1521:orcl";
Properties properties = new Properties();
properties.setProperty("user","scott");
properties.setProperty("password","tiger");
try {
AQjmsFactory.registerConnectionFactory(env, "scott3", url ,properties, "queue");
} catch ( JMSException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
register_jms_to_oid register_jms_to_oid = new register_jms_to_oid();
}
}

Now we can lookup this entry with standard jms calls.

package jms2;

import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.TextMessage;
import javax.jms.QueueConnectionFactory;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.NamingException;

public class lookup_jndi_queue {

private QueueConnection connection = null;
private QueueSession session = null;
private QueueSender sender = null;

public lookup_jndi_queue() {

Hashtable env = new Hashtable(5, 0.75f);
DirContext ctx;
QueueConnectionFactory queueConnectionFact;
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "o=sgi,c=us");
env.put(Context.SECURITY_CREDENTIALS, "secret");

try {
ctx = new InitialDirContext(env);
ctx = (DirContext)ctx.lookup("cn=connections, o=sgi,c=us");
Object reference = ctx.lookup("cn=scott");
queueConnectionFact = (QueueConnectionFactory)reference;
try {
connection = queueConnectionFact.createQueueConnection();
session = connection.createQueueSession(true, QueueSession.CLIENT_ACKNOWLEDGE);
connection.start();
QueueSession session = connection.createQueueSession(true, QueueSession.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("JMS_IN");
sender = session.createSender(queue);
String xmlData = "1111";
TextMessage message = session.createTextMessage(xmlData);
sender.send(message);
session.commit();
} catch (JMSException e) {
// TODO
e.printStackTrace();
}
} catch (NamingException ee) {
// TODO
ee.printStackTrace();
}
}

public static void main(String[] args) {
lookup_jndi_queue lookup_jndi_queue = new lookup_jndi_queue();
}
}


You can also use the ldapbrowser template. The entry look this in ldap.

Thursday, May 22, 2008

Employee search jsf page on Coherence with JPA

This I had this week a great coherence training for Oracle partners, where I learned a lot of interesting things about Coherence . This product is from tangosol ( Now owned byOracle ) and is a memory distributed data grid solution for clustered applications and application servers.

You may think coherence is a very complex product but it is a very easy to handle. It does everything automatically like backing up the cache on different nodes, distribute the load and recover from lost cache servers or adding new coherence servers to the cluster.
In this blog I will show how you can make a jsf employee search page on the coherence cache which get its data from a JPA datasource. Coherence runs above JPA. In this blog I will also update the cache entries with a salary raise and coherence will take care to update the right records in the employee table.
If you want to do it yourself you have to download coherence from Oracle. Add coherence.jar and tangosol.jar to the project libraries. Then we can create a entity bean ( entities from tables ( JPA / EJB3.0) on the employee table ( HR schema). Jdeveloper create a persistence.xml which we have to change with the right class and database parameters. persistence-unit name must match with the property of JpaCacheStore in the coherence jpa xml.

<?xml version="1.0" encoding="windows-1252" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="JPA">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<class>nl.ordina.coherence.model.Employees</class>
<properties>
<property name="toplink.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
<property name="toplink.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
<property name="toplink.jdbc.user" value="hr"/>
<property name="toplink.jdbc.password" value="hr"/>
</properties>
</persistence-unit>
</persistence>
Next I created jpa-cache-config-web.xml

<?xml version="1.0" encoding="windows-1252" ?>
<cache-config>
<caching-scheme-mapping>
<cache-mapping>
<!-- Set the name of the cache to be the entity name -->
<cache-name>Employees</cache-name>
<!-- Configure this cache to use the scheme defined below -->
<scheme-name>jpa-distributed</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>jpa-distributed</scheme-name>
<service-name>JpaDistributedCache</service-name>
<backing-map-scheme>
<read-write-backing-map-scheme>
<internal-cache-scheme>
<local-scheme/>
</internal-cache-scheme>
<cachestore-scheme>
<class-scheme>
<class-name>com.tangosol.coherence.jpa.JpaCacheStore</class-name>
<init-params>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>{cache-name}</param-value>
</init-param>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>nl.ordina.coherence.model.{cache-name}</param-value>
</init-param>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>JPA</param-value>
</init-param>
</init-params>
</class-scheme>
</cachestore-scheme>
</read-write-backing-map-scheme>
</backing-map-scheme>
<autostart>true</autostart>
</distributed-scheme>
</caching-schemes>
</cache-config>

Add this file to the run options of the cache-server.cmd and the jdeveloper web project runtime options. -Dtangosol.coherence.cacheconfig=D:\oracle\coherence\bin\jpa-cache-config-web.xml
Now we can load the Employees cache. Do this with the following code
private NamedCache employees = CacheFactory.getCache("Employees");
For searching through the cache for the right employees I use a filter.

public void search(ActionEvent actionEvent) {

Filter filter = null;
if ( firstName.getValue() != null && lastName.getValue() != null ) {
filter = new OrFilter( new LikeFilter("getFirstName", firstName.getValue().toString())
, new LikeFilter("getLastName" , lastName.getValue().toString()));
} else if ( firstName.getValue() != null && lastName.getValue() == null ) {
filter = new LikeFilter("getFirstName", firstName.getValue().toString());
} else if ( firstName.getValue() == null && lastName.getValue() != null ) {
filter = new LikeFilter("getLastName", lastName.getValue().toString());
} else {
seeAll(actionEvent);
return;
}

Set empSet = employees.entrySet(filter);

int size = empSet.size();
List list = new ArrayList();

for (Iterator it = empSet.iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry)it.next();
Employees empl = (Employees)entry.getValue();
list.add(empl);
}
model = new ArrayDataModel(list.toArray());
}

To update the employee records. I first need to have a class which I can invoke on the cache

package nl.ordina.coherence.backing;

import com.tangosol.util.processor.AbstractProcessor;
import com.tangosol.util.InvocableMap.Entry;
import nl.ordina.coherence.model.Employees;

public class RaiseSalary extends AbstractProcessor {
public RaiseSalary() {
}

public Object process(Entry entry ) {
Employees emp = (Employees)entry.getValue();
emp.setSalary(emp.getSalary() * 1.10);
entry.setValue(emp);
return null;
}
}


We invoke this by the following statement employees.invokeAll(AlwaysFilter.INSTANCE, new RaiseSalary());. Coherence updates the affected records.
Here you can download the example project. You have to start 1 cache server on your network and you can start the webapp on different clients. Coherence connects automatically to the cache server. You can search the cache immediately. Start for example more cache servers and look at the timing. And update the cache by doing a salary raise on 1 webapp and query the changed employees in the other webapp.

To run this example you have to change some files.
Change the path parameters with the right folders of jpa-cache-server-web2.cmd file (this included in the zip) and the classpath of the employee bean
:launch
set java_opts="-Xms%memory% -Xmx%memory% -Dtangosol.coherence.cacheconfig=D:\oracle\coherence\bin\jpa-cache-config-web.xml"
"%java_exec%" -server -showversion "%java_opts%" -cp "%coherence_home%\lib\coherence.jar;%coherence_home%\lib\coherence-jpa.jar;D:\oracle\jdevstudio10133\jdbc\lib\ojdbc14.jar;D:\oracle\jdevstudio10133\toplink\jlib\toplink-essentials.jar;D:\projecten\workspace\10.1.3.3\coherence\web\public_html\WEB-INF\classes" com.tangosol.net.DefaultCacheServer %1
Change the run options and the libraries of the project and off course the persistence.xml for the database parmaters.