Sunday, March 28, 2010

Create Weblogic JMS Foreign Server and Use It in Spring

The following steps are performed in Weblogic 9.2. Notice that you must have the valid values for the third party JMS server. These values are the value for java.naming.factory.initial and java.naming.provider.url of the third party JMS provider, the JNDI name of the destination and the connection factory on the third party JMS provider.

  1. Open Weblogic Admin Console.
  2. Navigate to Services/Messaging/JMS Modules.
  3. Click "New" to create a new module. Give the new module name "DemoModule". Then click on "Next". Choose the target server and click on "Next". Click "Finish" and then "Activate Changes" to finish the creation of the module.
  4. Now it is back on the JMS Modules main page. Click on "DemoModule".
  5. Click "Edit" and "New". The next page will show a list of resources that can be created. Click on "Foreign Server" and "Next".
  6. Give the foreign server a name "DemoForeignServer" and click on "Next".
  7. The next page will show that the target server has been selected. Just click on "Finish" and "Activate Changes".
  8. Click on "DemoForeignServer". Give the value for "JNDI Initial Context Factory" and "JNDI Connection URL". Note that the JNDI connection URL can be an LDAP url with the format "ldap://......". Click "Save" and "Activate Changes".
  9. Now on the same page, click on the tab "Connection Factories".
  10. Click "New". Specify the name, the local JNDI name and the remote JNDI name for the connection factory. The remote JNDI name must be a valid name that is already configured on the actual third party JMS provider. You can use a local JNDI name as you like. Suppose the local JNDI name is "demoLocalForeignConnectionFactoryJNDIName". You can optionally configure a user name and password to secure the foreign factory. Click "OK" and "Activate Changes".
  11. Click on "Destinations" tab. Click on "New".
  12. Specify the name, the local JNDI name and the remote JNDI name for the destination for the destination. The remote JNDI name must be a valid name that is already configured on the actual third party JMS provider. You can pick a local JNDI name as you like. Suppose the local JNDI name is "demoLocalForeignDestinationJNDIName". Click "OK" and "Activate Changes".
That's it! Now you can use this foreign JMS server in your application.

Sample Spring JMS configuration File

If you are using Springframework, you can configure the JMS beans as in the following file. Note that the message sender and listener use the same destination in this file. In the actual application, they should be different.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD Bean//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- Application Context -->
<beans>
 <bean id="jmsQueueConnectionFactory"
  class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName">
   <value>demoLocalForeignConnectionFactoryJNDIName</value>
  </property>
 </bean>
 <bean id="myDestination"
  class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName">
   <value>demoLocalForeignDestinationJNDIName</value>
  </property>
 </bean>
 <bean id="jmsTemplate"
  class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory">
   <ref bean="jmsQueueConnectionFactory" />
  </property>
  <property name="defaultDestination">
   <ref bean="myDestination" />
  </property>
 </bean>
 <bean id="jmsSender"
  class="com.mycompany.myapp.server.common.jms.JmsSender">
  <property name="jmsTemplate">
   <ref bean="jmsTemplate" />
  </property>
 </bean>
 <bean id="messageListener"
  class="com.mycompany.myapp.server.common.jms.ExampleListener">
 </bean>
 <bean id="jmsContainer"
  class="org.springframework.jms.listener.DefaultMessageListenerContainer">
  <property name="connectionFactory"
   ref="jmsQueueConnectionFactory" />
  <property name="destination" ref="myDestination" />
  <property name="sessionAcknowledgeModeName"
   value="AUTO_ACKNOWLEDGE" />
  <property name="messageListener" ref="messageListener" />
  <property name="sessionTransacted" value="true" />
 </bean>
</beans>

Suppose the name of the above file is myapp-spring-jms.xml. Then in the web.xml, add the following parameter:
<context-param>
  <param-name>contextConfigLocation<param-name>
  <param-value>/WEB-INF/myapp-spring-jms.xml</param-value>
 </context-param>

The Sample Message Sender

The sample java class to send the message. This class implements the interface IJmsSender, which is a user defined interface. Its only method is sendMessage(String). The interface is just for the Spring bean configuration purpose. With this interface, other Spring beans can use IJmsSender to inject an instance of JmsSender.
package com.mycompany.myapp.server.common.jms;

public interface IJmsSender {
 public void sendMessage(final String message);
}
package com.mycompany.myapp.server.common.jms;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

public class JmsSender implements IJmsSender
{
  private static Log log = LogFactory.getLog(Test.class);
  private JmsTemplate jmsTemplate;

  public JmsTemplate getJmsTemplate()
  {
    return jmsTemplate;
  }

  public void setJmsTemplate(JmsTemplate jmsTemplate)
  {
    this.jmsTemplate = jmsTemplate;
  }

  public void sendMessage(final String message)
  {
    jmsTemplate.send(new MessageCreator()
    {
      public Message createMessage(Session session) throws JMSException
      {
        log.debug("Creating the message from text " + message);
        Message textMsg = session.createTextMessage(message);
        return textMsg;
      }
    });
  }
}

The Sample Message Listener

The samle JMS listener to receive the message:
package com.mycompany.myapp.server.common.jms;

import javax.jms.JMSException;
import javax.jms.Message; 
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ExampleListener implements MessageListener
{
  private static Log logger = LogFactory.getLog(ExampleListener.class);

  public void onMessage(Message message)
  {
    logger.info("onMessage() message=" + message);
    if (message instanceof TextMessage)
    {
      try
      {
        logger.info(((TextMessage) message).getText());
        // process message
      }
      catch (JMSException ex)
      {
        throw new RuntimeException(ex);
      }
    }
    else
    {
      logger.info("non-TextMessage");
    }
  }
}

A Sample WLST Script to Create the JMS Foreign Server

Note that this file was obtained by editing the script generated by the WLST configToScripy command. Suppose the name of this file is config_tibco.py. It loads the property file config_tibco.py.properties. To run the configuration, first execute C:\bea921\weblogic92\common\bin\wlst.cmd. Then under the prompt wls:/offline> type
execfile('c:/workshop/myApp/temp/config_tibco.py')
"""
@author Copyright (c) 2004-2005 by BEA Systems, Inc. All Rights Reserved.

This script will first try to connect to a running server using the 
values in the properties file. If there is no server running, WLST
will start a server with the values in the properties file. You should change
these values to suit your environmental needs. After running the script, 
the server that is started(if started one) will be shutdown. 
This might exit you from your WLST shell."""

from weblogic.descriptor import BeanAlreadyExistsException
from java.lang.reflect import UndeclaredThrowableException
from java.lang import System
import javax
from javax import management
from javax.management import MBeanException
from javax.management import RuntimeMBeanException
import javax.management.MBeanException
from java.lang import UnsupportedOperationException

def initConfigToScriptRun():
global startedNewServer
loadProperties("c:/workshop/myApp/temp/config_tibco.py.properties")
hideDisplay()
hideDumpStack("true")
# try connecting to a running server if it is already running ... 
if connected=="false":
try:
URL="t3://"+adminServerListenAddress+":"+adminServerListenPort
connect(userName, passWord, URL)
except WLSTException:
print 'No server is running at '+URL+', the script will start a new server'
hideDumpStack("false")
if connected=="false":
print 'Starting a brand new server at '+URL+' with server name '+adminServerName
print 'Please see the server log files for startup messages available at '+domainDir
# If a config.xml exists in the domainDir, WLST will use that config.xml to bring up the server. 
# If you would like WLST to overwrite this directory, you should specify overWriteRootDir='true' as shown below
# startNewServer(adminServerName, domName, URL, userName, passWord,domainDir, overWriteRootDir='true')
_timeOut = Integer(TimeOut)
# If you want to specify additional JVM arguments, set them using startServerJvmArgs in the property file or below
_startServerJvmArgs=startServerJvmArgs
if (_startServerJvmArgs=="" and (System.getProperty("java.vendor").find("Sun")>=0 or System.getProperty("java.vendor").find("Hewlett")>=0)):
_startServerJvmArgs = " -XX:MaxPermSize=128m"
if overWriteRootDir=='true':
startServer(adminServerName, domName, URL, userName, passWord,domainDir, timeout=_timeOut.intValue(), overWriteRootDir='true', block='true', jvmArgs=_startServerJvmArgs)
else:
startServer(adminServerName, domName, URL, userName, passWord,domainDir, timeout=_timeOut.intValue(), block='true', jvmArgs=_startServerJvmArgs)
startedNewServer=1
print "Started Server. Trying to connect to the server ... "
connect(userName, passWord, URL)
if connected=='false':
stopExecution('You need to be connected.')

def startTransaction():
edit()
startEdit()

def endTransaction():
startEdit()
save()
activate(block="true")

from javax.management import InstanceAlreadyExistsException
from java.lang import Exception
from jarray import array

def endOfConfigToScriptRun():
global startedNewServer
#Save the changes you have made
# shutdown the server you have started
if startedNewServer==1:
print 'Shutting down the server that is started... '
shutdown(force='true', block='true')
print 'Done executing the script.'

def create_JMSSystemResource_0(path, beanName):
cd(path)
try:
print "creating mbean of type JMSSystemResource ... "
theBean = cmo.lookupJMSSystemResource(beanName)
if theBean == None:
cmo.createJMSSystemResource(beanName)
except java.lang.UnsupportedOperationException, usoe:
pass
except weblogic.descriptor.BeanAlreadyExistsException,bae:
pass
except java.lang.reflect.UndeclaredThrowableException,udt:
pass

def create_ForeignServer_4(path, beanName):
cd(path)
try:
print "creating mbean of type ForeignServer ... "
theBean = cmo.lookupForeignServer(beanName)
if theBean == None:
cmo.createForeignServer(beanName)
except java.lang.UnsupportedOperationException, usoe:
pass
except weblogic.descriptor.BeanAlreadyExistsException,bae:
pass
except java.lang.reflect.UndeclaredThrowableException,udt:
pass

def create_ForeignDestination_6(path, beanName):
cd(path)
try:
print "creating mbean of type ForeignDestination ... "
theBean = cmo.lookupForeignDestination(beanName)
if theBean == None:
cmo.createForeignDestination(beanName)
except java.lang.UnsupportedOperationException, usoe:
pass
except weblogic.descriptor.BeanAlreadyExistsException,bae:
pass
except java.lang.reflect.UndeclaredThrowableException,udt:
pass

def create_ForeignConnectionFactory_8(path, beanName):
cd(path)
try:
print "creating mbean of type ForeignConnectionFactory ... "
theBean = cmo.lookupForeignConnectionFactory(beanName)
if theBean == None:
cmo.createForeignConnectionFactory(beanName)
except java.lang.UnsupportedOperationException, usoe:
pass
except weblogic.descriptor.BeanAlreadyExistsException,bae:
pass
except java.lang.reflect.UndeclaredThrowableException,udt:
pass

def setAttributesFor_TibcoForeignServer_13():
cd("/JMSSystemResources/myAppTibcoJMSModule/JMSResource/myAppTibcoJMSModule/ForeignServers/TibcoForeignServer")
print "setting attributes for mbean type ForeignServer"
set("InitialContextFactory", "com.tibco.tibjms.naming.TibjmsInitialContextFactory")
set("DefaultTargetingEnabled", "true")
set("Name", "TibcoForeignServer")
set("ConnectionURL", "ldap://a_valid_url_here")

def setAttributesFor_myAppTibcoFCF_17():
cd("/JMSSystemResources/myAppTibcoJMSModule/JMSResource/myAppTibcoJMSModule/ForeignServers/TibcoForeignServer/ForeignConnectionFactories/myAppTibcoFCF")
print "setting attributes for mbean type ForeignConnectionFactory"
set("LocalJNDIName", "jms/testTibcoFCF")
set("Name", "myAppTibcoFCF")
set("RemoteJNDIName", "a_valid_remote_JNDI_ForConnectionFactory")

def setAttributesFor_testTibcoQueueForeignDestination_15():
cd("/JMSSystemResources/myAppTibcoJMSModule/JMSResource/myAppTibcoJMSModule/ForeignServers/TibcoForeignServer/ForeignDestinations/testTibcoQueueForeignDestination")
print "setting attributes for mbean type ForeignDestination"
set("LocalJNDIName", "jms/testTibcoQueueDestination")
set("Name", "testTibcoQueueForeignDestination")
set("RemoteJNDIName", "a_valid_remote_JNDI_for_destination")

def setAttributesFor_myAppTibcoJMSModule_11():
cd("/JMSSystemResources/myAppTibcoJMSModule")
print "setting attributes for mbean type JMSSystemResource"
refBean0 = getMBean("/Servers/myAppAdminServer")
theValue = jarray.array([refBean0], Class.forName("weblogic.management.configuration.TargetMBean"))
cmo.setTargets(theValue)

try:
initConfigToScriptRun()
startTransaction()
create_JMSSystemResource_0("/", "myAppTibcoJMSModule")
create_ForeignServer_4("/JMSSystemResources/myAppTibcoJMSModule/JMSResource/myAppTibcoJMSModule", "TibcoForeignServer")
create_ForeignDestination_6("/JMSSystemResources/myAppTibcoJMSModule/JMSResource/myAppTibcoJMSModule/ForeignServers/TibcoForeignServer", "testTibcoQueueForeignDestination")
create_ForeignConnectionFactory_8("/JMSSystemResources/myAppTibcoJMSModule/JMSResource/myAppTibcoJMSModule/ForeignServers/TibcoForeignServer", "myAppTibcoFCF")
setAttributesFor_myAppTibcoJMSModule_11()
setAttributesFor_TibcoForeignServer_13()
setAttributesFor_testTibcoQueueForeignDestination_15()
setAttributesFor_myAppTibcoFCF_17()
endTransaction()
finally:
endOfConfigToScriptRun()

Sample Property File for WLST Script

#WLST ConfigToScript Default Properties file 
#Mon Mar 29 09:53:26 EDT 2010
exitonerror=false
adminServerListenAddress=localhost
adminServerName=myAppAdminServer
domName=myAppwls9loc
overWriteRootDir=false
TimeOut=240000
startedNewServer=0
domainDir=C:/bea921/user_projects/domains/myAppwls9loc
userName=wlsadmin
passWord=wlsadmin1
adminServerListenPort=7001
startServerJvmArgs=

Reference

1. http://download.oracle.com/docs/cd/E13222_01/wls/docs92/ConsoleHelp/taskhelp/jms_modules/foreign_servers/ConfigureForeignServers.html
2. http://forum.springsource.org/showthread.php?t=34180

Thursday, March 18, 2010

Steps to Create a Webservcie Server Using Weblogic

These steps are performed on Weblogic 9.2. The directory workshop\java\WebServices\sampleservice is user created.

1. Open a cmd window. Type C:\bea921\user_projects\domains\mydomain\bin\SetDomainEnv.cmd. This will setup the environment and change current directory to C:\bea921\user_projects\domains\mydomain
2. Change the directory to C:\workshop\java\WebServices\sampleservice, which is the working directory.
3. Make sure that the following URL works: http://localhost:7100/sampleservice/SampleServiceService?WSDL
If it does not work, the application may have been stopped. Go to C:\bea921\user_projects\domains\mydomain\bin and type startWeblogic.cmd to start the application.
4. An eclipse project called sampleservicews is already created. The two files used are SampleServiceImpl and MyDatatype in the package com.mycompany.myapp.webservice.sampleservice. Modify these two classes if needed.
5. type "ant clean". This will clean up the old folders that may be generated before and also undeploy the already deployed webservcie application running at localhost:7100. Verify this by browsing the wsdl url again. It should give error.
6. type "ant build-service".
7. type "ant deploy". This will deploy the newly created webservice.
8. Verify that the new webservice has been deployed successfully by browsing to the wsdl URL. It should show the WSDL file. A page refresh may be needed to see the new WSDL file.
9. Now the new webservice is working, we can use the weblogic tools to generate the client code.

Notes on the build.xml file.

The target <client> and <run> may not work if the related files are not implemented.

Sample build.xml

<project name="webservices-sampleservice" default="all"><!-- set global properties for this build -->
 <property name="wls.username" value="weblogic" />
 <property name="wls.password" value="weblogic" />
 <property name="wls.hostname" value="localhost" />
 <property name="wls.port" value="7100" />
 <property name="wls.server.name" value="PlayServer" />
 <property name="ear.deployed.name" value="sampleserviceServiceEar" />
 <property name="example-output" value="output" />
 <property name="ear-dir"
  value="${example-output}/sampleserviceServiceEar" />
 <property name="clientclass-dir"
  value="${example-output}/clientclass" />
 <path id="client.class.path">
  <pathelement path="${clientclass-dir}" />
  <pathelement path="${java.class.path}" />
 </path>
 <taskdef name="jwsc"
  classname="weblogic.wsee.tools.anttasks.JwscTask" />
 <taskdef name="clientgen"
  classname="weblogic.wsee.tools.anttasks.ClientGenTask" />
 <taskdef name="wldeploy"
  classname="weblogic.ant.taskdefs.management.WLDeploy" />
 <target name="all" depends="clean,build-service,deploy,client" />
 <target name="clean" depends="undeploy">
  <delete dir="${example-output}" />
 </target>
 <target name="build-service">
  <jwsc srcdir="src" destdir="${ear-dir}" keepGenerated="true">
   <jws
    file="com/mycompany/myapp/webservice/sampleservice/SampleServiceImpl.java" />
  </jwsc>
 </target>
 <target name="deploy">
  <wldeploy action="deploy" name="${ear.deployed.name}"
   source="${ear-dir}" user="${wls.username}" password="${wls.password}"
   verbose="true" adminurl="t3://${wls.hostname}:${wls.port}"
   targets="${wls.server.name}" />
 </target>
 <target name="undeploy">
  <wldeploy action="undeploy" failonerror="false"
   name="${ear.deployed.name}" user="${wls.username}"
   password="${wls.password}" verbose="true"
   adminurl="t3://${wls.hostname}:${wls.port}"
   targets="${wls.server.name}" />
 </target>
 <target name="client">
  <clientgen
   wsdl="http://${wls.hostname}:${wls.port}/sampleservice/SampleServiceService?WSDL"
   destDir="${clientclass-dir}"
   packageName="examples.webservice.sampleservice.client" />
  <javac srcdir="${clientclass-dir}" destdir="${clientclass-dir}"
   includes="**/*.java" />
  <javac srcdir="src" destdir="${clientclass-dir}"
   includes="examples/webservices/sampleservice/client/**/*.java" />
 </target>
 <target name="run">
  <java fork="true"
   classname="examples.webservices.sampleservice.client.Main"
   failonerror="true">
   <classpath refid="client.class.path" />
   <arg
    line="http://${wls.hostname}:${wls.port}/sampleservice/SampleServiceService" />
  </java>
 </target>
</project>

Sample JWS file


In this JWS file, there is only one WebMethod. Notice that it returns the user-defined data type. I found that if the method signature does not contain the user-defined data type, then the WSDL file won't define that data type. Also generics are not supported when used as a parameter or return value. For example, the following Java method cannot be exposed as a public operation:
public ArrayList<String> foo(ArrayList<String> in){ return in};
In the following example, we explicitly use the MyDataType array so the WSDL file generated will have binding information for the class MyDataType.
package com.mycompany.myapp.webservice.sampleservice;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

import weblogic.jws.WLHttpTransport;

//the targetNamespace here will be used as the package name for the custom datatype MyDatatype in the weblogic 
//generated webservcie client code. Note that here the targetNamespace matches the package name of the class SampleServiceImpl. 
//But this is not necessary. In fact, if in the targetNamespace, myapp is changed to xyz, then the wsdl and the 
//webservice client code will have com.mycompany.xyz.webservice.sampleservice and the class 
//ArrayOfMyDatatype_Literal will be in that package in stead of com.mycompany.myapp.webservice.sampleservice
@WebService(serviceName = "SampleServiceService", name = "SampleServicePortType", targetNamespace = "http://sampleservice.webservice.myapp.mycompany.com")
// Standard JWS annotation that specifies this is a document-literal-wrapped Web Service
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
// WebLogic-specific JWS annotation that specifies the context path and service URI used to build the URI of the Web Service 
@WLHttpTransport(contextPath = "sampleservice", serviceUri = "SampleServiceService", portName = "SampleServiceServicePort")
/**
 * This JWS file forms the basis of a WebLogic Web Service. The Web Services has
 * one public operations: getMyDatasAtLocation The Web Service is defined as a
 * "document-literal" service, which means that the SOAP messages have a single
 * part referencing an XML Schema element that defines the entire body.
 * 
 * @author Copyright (c) 2005 by BEA Systems. All Rights Reserved.
 */
public class SampleServiceImpl
{
  @WebMethod()
  public MyDatatype[] getMyDatasAtLocation(Long locationSeq)
  {
    System.out.println("getMyDatasAtLocation called with the location seq "
        + locationSeq);
    MyDatatype[] myDatas = new MyDatatype[3];
    for (int i = 0; i < 3; i++)
    {
      MyDatatype myData = new MyDatatype();
      myData.setStatus("ACTIVE");
      myData.setMyDataNbr("" + locationSeq + i);
      myData.setMyDataType("SAMPLE");
      myDatas[i] = myData;
    }
    return myDatas;
  }
}

Sample User-Defined Data Type

package com.mycompany.myapp.webservice.sampleservice;

public class MyDatatype
{
  private String myDataNbr;
  private String myDataType;
  private String status;

  public String getMyDataNbr()
  {
    return myDataNbr;
  }

  public void setMyDataNbr(String myDataNbr)
  {
    this.myDataNbr = myDataNbr;
  }

  public String getMyDataType()
  {
    return myDataType;
  }

  public void setMyDataType(String myDataType)
  {
    this.myDataType = myDataType;
  }

  public String getStatus()
  {
    return status;
  }

  public void setStatus(String status)
  {
    this.status = status;
  }

  public String toString()
  {
    return "type=" + myDataType + " nbr=" + myDataNbr + " status=" + status;
  }
}

Notes

When a client application invokes a WebLogic Web Service operation, the operation invocation takes place outside the context of a transaction, by default. But you can use the @weblogic.jws.Transactional annotation in the JWS file to specify that the operation should be run inside a transaction. See http://download.oracle.com/docs/cd/E12840_01/wls/docs103/webserv_ref/annotations.html#wp1057803. However, I am not sure if this really works. The documentation of the sample MedRec application that comes with weblogic10 says contradictorily that "WebLogic Web Services do not support distributed transaction and its integration with JTA" in the chapter "Delegate Services". Maybe the best way to verify this is to do a real test.

Tuesday, March 16, 2010

Steps to Create a Webservice Client Using Weblogic

These steps are performed on weblogic 9.2.
  1. First a working WSDL url is needed. In the file C:\workshop\java\WebServices\Sample_Client\build.xml, modify the related host name and the url so it points to the correct WSDL url.
  2. Open a cmd window. Type C:\bea921\weblogic92\server\bin\setWLSEnv.cmd. This will setup the environment.
  3. type “ant clean”. This will clean up the old folders that may be generated before.
  4. type “ant build-client”. There might be errors. If the error is because of creating the client code itself, then the build file needs to be checked. Otherwise the error will be most likely from compiling the Main.java file. Modify this file and run “ant build-client” again. Repeat this until Main.java is compiled successfully.
  5. type “ant run” to see if the Main.java can be run.
  6. type “ant jar-client” to create the jar file. The jar file is named ws_sample.jar and is in the folder C:\workshop\java\WebServices\Sample_Client\output . This jar file will be put into the application classpath and used by the application.

Notes on the build.xml file.

  1. The target has an attribute packageName. The build.xml file has specified this to be packageName="com.myapp.wsclient.myproj". This is the package that will hold the webservice interface that the client will use to call the webservice.
  2. The webservice has its own request or response custom data types. These java classes are generated in the folder that is specific to the webservice. In this example, these classes are in the folder output\clientclass\.
  3. The Main.java is just a class for testing. Its package name does not matter.

When you run the client to call the webservice, if you want to see the detailed log information such as the actual SOAP message sent to the web service host, you can set the verbose parameter value. In weblogic 10.3, you can either do it on the command line:
-Dweblogic.wsee.verbose=* 
or do it programmatically:
System.setProperty("weblogic.wsee.verbose", "*");

Sample build.xml

<project name="webservices-client_play" default="all"><!-- set global properties for this build -->
 <property name="wls.hostname" value="sample_host" />
 <property name="wls.port" value="80" />
 <property name="example-output" value="output" />
 <property name="clientclass-dir"
  value="${example-output}/clientclass" />
 <path id="client.class.path">
  <pathelement path="${clientclass-dir}" />
  <pathelement path="${java.class.path}" />
 </path>
 <taskdef name="clientgen"
  classname="weblogic.wsee.tools.anttasks.ClientGenTask" />
 <target name="clean">
  <delete dir="${clientclass-dir}" />
 </target>
 <target name="all" depends="clean,build-client,run" />

 <target name="build-client">
  <clientgen
   wsdl="http://${wls.hostname}:${wls.port}/app_path/sample.wsdl"
   destDir="${clientclass-dir}" packageName="com.myapp.wsclient.myproj" 
   type="JAXRPC" />
  <javac srcdir="${clientclass-dir}" destdir="${clientclass-dir}"
   includes="**/*.java" />
  <javac srcdir="src" destdir="${clientclass-dir}"
   includes="examples/webservices/client_play/*.java" />
 </target>
 <target name="run">
  <java fork="true"
   classname="examples.webservices.client_play.Main"
   failonerror="true">
   <classpath refid="client.class.path" />
   <arg
    line="http://${wls.hostname}:${wls.port}/app_path/sample.wsdl" />
  </java>
 </target>
 <target name="jar-client">
  <jar destfile="${example-output}/ws_sample.jar"
   basedir="${clientclass-dir}" excludes="**/Main.*" />
 </target>
</project>

Note that in the clientgen target, there is the attribute type. Its default value is JAXRPC. To generate a JAXWS client, you just need to change this value to "JAXWS".

Sample Main.java



package examples.webservices.client_play;

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import java.util.*;
import examples.webservices.client_play.*;

//import more here

public class Main
{
  public static void main(String[] argsthrows ServiceException,
      RemoteException
  {
    String url = args[0];
    SamplePortTypeService service = new SamplePortTypeService_Impl(url);
    SamplePortType port = service.getSamplePortTypePort();
    System.out.println("port is " + port);
  }
}

Thursday, March 11, 2010

A Trick in JSF Event Handling

The following is a samle event handler.
public void somethingChanged(ValueChangeEvent e)
  {
    PhaseId phaseId = e.getPhaseId();
    if (phaseId.equals(PhaseId.ANY_PHASE))
    {
      e.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
      e.queue();
    }
    else if (phaseId.equals(PhaseId.UPDATE_MODEL_VALUES))
    {
      // some processing code here
    }
  }

A Sample Database Trigger for Auditing

Suppose there is a table Event. All the updates or deletes on this table need to be auditted. We can use trigger for this purpose.
We create the Event_His table which has all the columns in the Event table. We also add three new columns to indicate the reason, the person and the date when a row in the Event table is changed.

Table Event
event_seq
created_by
created_dt
updated_by
updated_dt
lock_version
type
name
description
start_date
end_date
status

Table Event_His
event_seq
created_by
created_dt
updated_by
updated_dt
lock_version
type
name
description
start_date
end_date
status
reason
actioner
action_date

The Package Used by the Trigger:
CREATE OR REPLACE PACKAGE Auditpackage AS
Updater VARCHAR2(10);
PROCEDURE Set_updater(person  in VARCHAR2);
END;

CREATE OR REPLACE PACKAGE BODY Auditpackage AS
PROCEDURE Set_updater(person  in VARCHAR2)  is
BEGIN
Updater   := person;    
END;
END;

Before deleting a row, one needs to first call Auditpackage.Set_updater(?) to set who will delete the record. Then the trigger will use the saved value when creating a new row in the history table.

The trigger is the following:

BEGIN
IF DELETING  THEN

insert into Event_His values
(Event_his_seqgen.NEXTVAL,:old.Event_seq, :old.created_by, :old.created_dt,
:old.updated_by, :old.updated_dt, :old.lock_version,
:old.type, :old.name, :old.description, :old.start_date, :old.end_date, :old.status,
'delete', Auditpackage.updater, sysdate);

ELSE

insert into Event_His values
(Event_his_seqgen.NEXTVAL,:old.Event_seq, :old.created_by, :old.created_dt,
:old.updated_by, :old.updated_dt, :old.lock_version,
:old.type, :old.name, :old.description, :old.start_date, :old.end_date, :old.status,
'update', :new.updated_by, sysdate);

END IF;

END;

As an option, the package variable Updater can be reset to null after a record is updated or deleted in the Event table. For this purpose, the following trigger is needed.

CREATE OR REPLACE TRIGGER Audit_event_reset
AFTER DELETE OR UPDATE ON Event
BEGIN
Auditpackage.Set_updater(NULL);
END;

Notice that the previous two triggers are both fired by the same type of SQL statement. However, the AFTER row trigger is fired once for each row of the table affected by the triggering statement, while the AFTER statement trigger is fired only once after the triggering statement execution is completed.

Reference

http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14251/adfns_triggers.htm#i1007170