In my first blog I have described how to utilize TimeoutService to schedule a periodical task running on WAS in a background. The unit responsible for initializing of TimeoutService was a J2EE servlet.
In this blog I will show how TimeoutService can help to resolve very common issue - exchange structured parameters between different components (WD-J2EE, WD-WD etc.). The problem is that there is no other scope in WD world rather than scope of WD component. So, you can store state of your component only in context (you can store it in class variables of controllers but it is bad practice which should be avoided). After some investigation of SDN forum, I have figured out that there is still no solution how to pass structured objects between components except using non-public WD API:
I will try to build a custom SAP J2EE WAS service which would be responsible for storing and fetching structured objects. Some remarks:
In spite of all those warnings and disclaimers I will try to build a custom service and use a TimeoutService inside. TimoutService will run periodically and delete expired object from service. Service is responsible for object storing, generating of unique object key and fetching previously stored object by key. We will implement loading of properties describing service behavior like synchronization period and expiration time of object, handling events occurring on server when properties are changed.
For the solution we will create 6 DC`s:
icp/javabean DC is very simple and contains only one JavaBean class with 2 properties (firstName and lastName). We will use this class to generate JavaBean model for our WD DC and to cast fetched object from service in Web Module DC. The reason to have structured object in a separate DC is just to decouple a UI DC`s (wd and web) from data DC (javabean). In your project you can easily include description of structured object in any other DC. The only critical point here is that all components involved to an integration scenario should have a reference to the component which contains description of structured object (except same components which are packaged together).
It is the most important part of our infrastructure. To use TimeoutManager and TimeoutListener interfaces we need to add public part from appropriate DC. It is
”SAP-J2E”->”com.sap.engine.client.lib”</b>.</p>Service implementation component consists of:
public interface InterComponentParameterService {
public abstract Object getObject(final String key);
public abstract String setObject(final Object value);
}
This is an entry point to the service and therefore it is exposed via api public part (Purpose - compilation).
public class ParameterTimeoutListsner implements TimeoutListener {
public void timeout() {
Collection values = _values.entrySet();
for (Iterator iter = values.iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
DataContainer dataContainer = (DataContainer) entry.getValue();
if( (System.currentTimeMillis() - dataContainer.getCreated())> _lifeTime ) {
iter.remove();
}
}
}
public boolean check() {
return true;
}
}
To store object in data container setObject method is used. It generates a unique key and stores object using a generated key. Key is returned back to reuse it for object fetching using method getObject:
public String setObject(final Object value) {
final String key = new UID().toString();
_values.put(key, new DataContainer(System.currentTimeMillis(), value));
return key;
}
public Object getObject(final String key) {
final DataContainer dataContainer = (DataContainer)_values.get(key);
return null!=dataContainer ? dataContainer.getValue() : null ;
}
Behavior of service is described by 2 property - timeout (how often timeout event is occurred) and lifetime (what is the length of object life cycle). To set properties following methods are used:
To allow iteration with SAP J2EE Engine we need to implement service frames (com.sap.engine.frame.ApplicationServiceFrame and com.sap.engine.frame.CommunicationServiceFrame) for dispatcher (communication) and server (application) J2EE Engine elements. In start() method we make initialization steps for our service implementation and register it in J2EE Engine:
public void start(ApplicationServiceContext serviceContext) {
try {
TimeoutManager timeManager =((TimeoutManager) serviceContext.getContainerContext().
getObjectRegistry().getServiceInterface("timeout"));
Properties properties = serviceContext.getServiceState().getProperties();
long timeout = readLongProperty(properties, "timeout", "10000");
long lifeTime = readLongProperty(properties, "lifeTime", "300000");
_remoteInterface = new InterComponentParameterServiceImpl(
timeManager, timeout, lifeTime);
serviceContext.
getContainerContext().
getObjectRegistry().
registerInterface( _remoteInterface );
serviceContext.
getServiceState().
registerContainerEventListener( new PropertyChangedEventListener() );
} catch (Exception e) {
location.traceThrowableT(300, "The remote interface can not be registered", e);
}
}
As we said we are using 2 properties to describe behavior of our service. So, we need a way how to notify service implementation that property values are changed. For such purposes we can use server events. We implement com.sap.engine.frame.container.event.ContainerEventListener interface:
public class PropertyChangedEventListener implements ContainerEventListener {
public boolean setServiceProperty(String key, String value) {
if("timeout".equals(key)) {
return _remoteInterface.setTimeout(value);
} else if("lifeTime".equals(key)) {
return _remoteInterface.setLifeTime(value);
} else {
return true;
}
}
public boolean setServiceProperties(Properties properties) {
boolean retValue = true;
if( properties.containsKey("timeout") ) {
retValue = _remoteInterface.setTimeout( properties.getProperty("timeout") );
}
if( properties.containsKey("lifeTime") ) {
retValue = retValue &&
_remoteInterface.setLifeTime( properties.getProperty("lifeTime") );
}
return retValue;
}
}
And register instance of event listener in start() method:
serviceContext.
getServiceState().
registerContainerEventListener( new PropertyChangedEventListener() );
WD component in DC is a "publisher" of structured object. It is responsible for storing object in service and pass generated key to the service "consumer".
To allow WD DC to uses service and store structured object within it we need to accomplish 2 tasks: add a public parts icp/javabean->api and icp/serviceimpl->api and add service reference to our custom build service. icp/javabean->api public part will let us create a JavaBean model (and access JavaBean class directly) and icp/serviceimpl->api public part will let us use the service. Service reference will enable cross-components class loaders (to prevent class loaders exceptions and errors).
We create simple WD component (com.sap.sdn.icp.wd.components.test.Test) containing only one view (com.sap.sdn.icp.wd.components.test.view. TestCV), application (com.sap.sdn.icp.wd.app.PostParameter) and a JavaBean model based on com.sap.sdn.icp.bean.Employee class from icp/javabean->api. On the view layout we create 2 input fields and one button. Input field properties "value" are mapped to according attributes in a model node.
Servlet in Web module DC is a "consumer" of structured object. It is responsible for reading object key from servlets parameters and fetching the object from service.
We need to add same public parts like for WD DC (icp/javabean->api and icp/serviceimpl->api) for same purposes. Service refernce to the the our custom build service we will add in EAR DC because Web Module DC is packeged within it.
In out Web Module DC we create a simple HTTP Servlet (by extending javax.servlet.http.HttpServlet class) and implement doGet() method within it:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String objectKey = request.getParameter("key");
InitialContext context = new InitialContext();
InterComponentParameterService icp =
(InterComponentParameterService) context.lookup("InterComponentsParametersService");
Employee employee = (Employee)icp.getObject(objectKey);
response.getOutputStream().print( employee.getFirstName() );
response.getOutputStream().print( " ");
response.getOutputStream().print( employee.getLastName() );
} catch (Exception e) {
e.printStackTrace(new PrintStream(response.getOutputStream()));
}
}
We use EAR DC to package Web Module DC into a deployable unit and to specify service references.
This DC is used to package our service to deployable unit.
In server/service-provider.xml file we are describing important information about our service: frame implementation class, jar which contains it, references to other services, version etc.
After building and deployment of icp/service (pay attention that by deployment of service J2EE server is restarted automatically), icp/ear and icp/wd we can perform a simple scenario test.
First we can check status of our custom build service using Visual Administrator. We can change properties if necessary.
We run our WD application, type some data into input fields and press "Transfer" button. New window appears with the servlet displaying data from WD.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 525 | |
| 263 | |
| 238 | |
| 234 | |
| 167 | |
| 157 | |
| 152 |