Previous Next vertical dots separating previous/next from contents/index/pdf

Providing Support for Control Callbacks from a Page Flow

Beginning with SP2, WebLogic Workshop version 8.1 supported a scenario in which a page flow could interact with a custom control that received control callbacks. Through polling methods on the custom control, the page flow could request the received callback information. By using the custom control as an intermediary, the page flow could interact with the external services that sent callbacks — something it was not, on its own, capable of doing because page flows could not receive callbacks. Transparent support for this feature is discontinued in version 10.0; this section describes how this support was provided in version 8.1 and how you can augment your upgraded version 10.0 application to enable your page flow to continue to retrieve information sent in callbacks.

Problem: A Gap Between Polling and Event-Based Interfaces

This version 8.1 feature was provided as a way to bridge the gap between the polling model supported by page flows and the event-based model supported by controls and web services. On their own, neither the page flow nor the custom control wrapping a callback-enabled control could create and manage a conversation. A page flow can only be accessed when handling a browser request — not afterward, say, to wait for a callback. A custom control requires a conversational container to participate in a conversation, and the page flow is not such a container. Prior to version 8.1 SP2, these design constraints made it impossible for callbacks to be correlated and delivered from a control to a client page flow.

To complicate matters, it was easy for developers to simply assume that a page flow could participate in a conversation and receive a callback; this feature was added in SP2 to address that concern and fill a perceived gap. However, as described below, the feature involved "behind the scenes" code generation that was not documented and is no longer supported in Workshop for WebLogic version 10.0.

Version 8.1 Solution: Hidden Code Generated to Bridge the Gap

In order for a page flow to interact with a control that received callbacks, a conversational context was needed to maintain state and correlate callbacks with clients. Neither the page flow nor a custom control could on their own support a conversation. The absence of this correlation support through a conversation was addressed through behind-the-scenes code generation. Specifically, as described below, WebLogic Workshop created a hidden web service to support a conversation. This web service was hidden during development, but was discoverable during deployment.

The following describes a simple application whose design is supported by the version 8.1 page flow-callback feature.

Application Components

A very simple application built on the feature described here would include the following components:

Runtime Processing

The following illustrates the flow of processes through this simple application.

  1. Page flow’s client makes a request to begin the application’s work.
  2. Page flow calls a method of the custom control that (the page flow believes) will interact with the control at the other end of the application.
  3. Custom control calls a method of the callback-sending control. This method call is intercepted and sent to the hidden web service. This starts a conversation through which the control's callbacks can be correlated.
  4. Hidden web service forwards the control method call to the control itself.
  5. Control sends its callback to the intermediary custom control, but the callback method is intercepted by the hidden conversational web service.
  6. Web service forwards callback to the custom control.
  7. Custom control collects data received from callbacks, holding it for retrieval by the page flow.
  8. Page flow polls the custom control for data collected via callbacks.

Version 10.0 Solution: Support Conversations by Adding a Web Service

You can continue support for this feature in your upgraded application by adding a web service to replace the one that used to be generated in version 8.1. The web service you add would have in one sense the same role as the hidden web service that was generated for you in version 8.1: maintaining state and correlating control callbacks. Needless to say, however, you’d have greater control and flexibility in the design because the service would be an explicit part of your application.

When designing your workaround, consider taking one of the following two basic approaches:

Conversation Best Practices

In version 8.1, the fact that a conversation existed was hidden; it was started when the application’s custom control was created and finished when the page flow’s HTTP session ended. In your version 10.0 application, you’ll need to start and finish the conversation with your own code.

Starting the conversation. One recommendation is to start the conversation when your custom control is created — in the custom control’s onCreate implementation, for example; onCreate is a callback invoked when the control is created before use. You can also start the conversation elsewhere in your code. If you do so, however, be sure that your code to finish the conversation first confirms that the conversation ID has been set.

Ending the conversation. You can explicitly end the conversation in the page flow’s onDestroy implementation; onDestroy is a callback invoked when the page flow is removed from the user session. Finishing the conversation in onDestroy "cleans up" by helping to ensure that the conversation isn’t left stranded and active after the user’s session has completed. For example, can create a method in the web service whose purpose is to finish the conversation, then call that method from the page flow controller's onDestroy handler.

As a kind of insurance against orphaned conversations, you should also set the conversation's lifetime to some limit that makes sense for your application. This should be a value that allows plenty of time for the application to successfully complete, but short enough to keep the conversation from running overly long. For example, consider setting the lifetime to one day.

Simple Web Service Example

Here's a very simple example of a web service designed to receive callbacks from a timer control, holding them for retrieval by a page flow controller. This is an example of the second approach described above, in which the custom control is replaced by a web service. In an application that keeps the custom control, the methods of this web service are called by that control on behalf of the page flow.

package services; 

import com.bea.control.TimerControl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.jws.WebMethod;
import javax.jws.WebResult;
import javax.jws.WebService;
import org.apache.beehive.controls.api.bean.Control;
import org.apache.beehive.controls.api.events.EventHandler;
import weblogic.jws.Conversation;
import weblogic.jws.Transactional;
import weblogic.jws.WLHttpTransport;
import weblogic.jws.soap.SOAPBinding;
import weblogic.jws.wlw.UseWLW81BindingTypes;
import weblogic.jws.wlw.WLWRollbackOnCheckedException;
import weblogic.jws.Conversational;

@Transactional(true)
@UseWLW81BindingTypes()
@WLHttpTransport(serviceUri = "services/CallbackGenerator.jws")
@WLWRollbackOnCheckedException()
@WebService(serviceName = "CallbackGenerator",
            targetNamespace = "http://www.openuri.org/")
@javax.jws.soap.SOAPBinding(style = javax.jws.soap.SOAPBinding.Style.DOCUMENT, 
                            use = javax.jws.soap.SOAPBinding.Use.LITERAL, 
                            parameterStyle = javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED)
// Conversation lifetime set to a one day maximum.
@Conversational(maxAge = "1 day")
public class CallbackWrapperService implements java.io.Serializable
{ 
    // A List to hold data returned via callbacks until the page flow controller
    // retrieves it.
    private List<Long> events = new ArrayList<Long>();

    @Control()
    @TimerControl.TimerSettings()
    private com.bea.control.TimerControl myTimer;

    static final long serialVersionUID = 1L;

    // The controller calls this method to start the conversation and
    // make the first call to the timer. In general, this method would
    // receive parameters needed for the control's first method call.
    @Conversation(Conversation.Phase.START)
    @SOAPBinding(style = javax.jws.soap.SOAPBinding.Style.DOCUMENT, 
                 use = javax.jws.soap.SOAPBinding.Use.LITERAL, 
                 parameterStyle = javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED)
    @WebMethod()
    @WebResult(name = "startResult")
    public void start(long timerInterval)
    {
        System.out.println("CallbackGenerator.jws started:timerInterval:" +
                           timerInterval);
        myTimer.setTimeout(5);
        myTimer.setRepeatsEvery(timerInterval);
        myTimer.start();
    }

    // The callback handler for the timer's onTimeout callback.
    // A callback handler would collect needed data in a form that
    // could be retrieved by the page flow controller.
    @EventHandler(field = "myTimer", 
                  eventSet = TimerControl.Callback.class, 
                  eventName = "onTimeout")
    public void myTimer_onTimeout(long time, Serializable data)
    {
        events.add(new Long(time));
    }

    // The controller calls this method to retrieve callback
    // data. 
    @WebMethod()
    @Conversation(Conversation.Phase.CONTINUE)
	public Long[] getEvents()
    {
        Long[] results = (Long[])events.toArray(new Long[events.size()]);
        events.clear();
        return results;
    }

    // The controller calls this method to finish the conversation after
    // retrieving callback data.
    @Conversation(Conversation.Phase.FINISH)
    @SOAPBinding(style = javax.jws.soap.SOAPBinding.Style.DOCUMENT, 
                 use = javax.jws.soap.SOAPBinding.Use.LITERAL, 
                 parameterStyle = javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED)
    @WebMethod()
    @WebResult(name = "finishResult")
    public void finish()
    {
        myTimer.stop();
    }
}

Related Topics

None.

 

Skip navigation bar   Back to Top