Three Workflow Approaches with WebLogic Portal

This is a blast from the past originally published at Developer.com when they were still interested in portal development.  I came across it because I needed a reference to Nested Page Flows and couldn’t find one until I ran across a link to my own article. Deja dude. Anyway, here it is. One day I will clean up the mark up, but for now it is still useful for reference and so long as the link above works you can still see the clean version…

While the disclaimers usually start later, this article merits one up front: These are not the only solutions to creating workflows in WLP. For example, we’re not even going to touch on JSF, or consider the possibility of special considerations in a federated portal architecture. So don’t let yourself be limited by the scope of this article or author’s experiences, and prejudices. What we will examine is some solutions that are known to work and should give you enough of the basics to implement any set of workflow requirements on WLP.

Simple Page Flows

Page flows provide a very straight forward approach to creating a workflow. Using the built-in wizard will quickly generate your page flow controller with the default begin action. This default action is a simple action, which doesn’t do much for flow as all it does is forward to the generated index.jsp.

This is quickly enhanced by right-clicking on the begin action under the Actions folder in the page flow perspective and selecting Convert to a Method.

@Jpf.Action(forwards = { @Jpf.Forward(name = "default", path = "index.jsp") })
public Forward begin()
{
return new Forward("default");
}

Now you can begin adding workflow logic to your page flow. This approach is good for a simple process where the user will enter data in multiple forms and each submit does some level of processing on new data entered. You can even provide branching logic, forwarding to an action based on inputs. In either case, a single form bean in the page flow controller serves well to maintain the values, placing “frozen” values into hidden fields to maintain them from page to page and action to action.

Below is a series of action stubs that follow a simple workflow to create a web site user account:

/**
* Check if existing first last and email
* @param form userDataFormBean
* @return success if new user, error if existing user
*/
@Jpf.Action(forwards = { @Jpf.Forward(name = "success", path = "creatUserName.jsp"), @Jpf.Forward(name="error", path="index.jsp")
})
public Forward processUserNameAndEmail(userDataFormBean form)
{
Forward forward = new Forward("success");
return forward;
}

/**
* create user name and request address information
* @param form userDataFormBean
*/
@Jpf.Action(forwards = { @Jpf.Forward(name = "success", path = "getAddress.jsp")
})
public Forward createUserName(userDataFormBean form)
{
Forward forward = new Forward("success");
return forward;
}

/**
* Save the snail mail address and offer to subscribe
* @param form userDataFormBean
*/
@Jpf.Action(forwards = { @Jpf.Forward(name = "success", path = "subscribeNewsletter.jsp")
})
public Forward storeAddressInfo(userDataFormBean form)
{
Forward forward = new Forward("success");
return forward;
}

/**
* Save the subsription choice and send to summary page
* @param form userDataFormBean
*/
@Jpf.Action(forwards = { @Jpf.Forward(name = "success", path = "summaryPage.jsp")
})
public Forward offerSubscription(userDataFormBean form)
{
Forward forward = new Forward("success");
return forward;
}

What makes this simple is that each JSP uses the same form bean, with the action set to the next action. In a more robust implementation each action would also have an error page to forward to, which could easily be the JSP that submitted the information (such as processUserNameAndEmail does) with error messages. This example could be expanded with some simple branching; such as if the user already exists in the database the page flow action could forward to a password reminder page instead of simply going back to the index page.

Nested Page Flows

Nested page flows take planning a coordination between the originating and nested controllers.This makes them very useful when the work flow is predetermined and not expected to change much or often. In other words, the nested page flow approach is best suited to Waterfall projects where most (if not all) requirements are known prior to development.

Nested page flows allow passing control off to another controller while maintaining the state of the originating controller. This can be useful for branching logic or if you are creating multiple workflows that have the same set of steps as part of the flow. You can develop a page flow control that does the common steps, then call it from the controllers that deal with the parts of the workflow that vary. For instance, in our earlier simple page flow we could add a survey in the work flow before the subscription page to determine what types of subscriptions to offer. This survey workflow could also be presented to existing users at log in if their responses were out of date or when there was a new survey. In both the account creation scenario and the login scenario, the survey comes in at the middle of the process, so we want to be able to reuse the survey code without losing the state of either the enrollment or login workflow, so we call the survey flow as a nested flow.

If you know you are going to be calling a page flow as a nested flow at the beginning you can get the necessary annotations and action methods generated by checking the “Make this a nested page flow” option at the start of the page flow creation wizard. The two key ingredients to making a pageflow nested is in the controller annotation at the class declaration:

@Jpf.Controller(nested = true)
public class NestedPageFlowAController extends PageFlowController{

And the necessity to have an action with a forward that includes a return action:

@Jpf.Action(forwards = { @Jpf.Forward(name = "done", returnAction = "portlets_nestedPageFlowADone")})
protected Forward done() {return new Forward("done");}

The return action must be an action method that exists in the controller that called the nested controller. Calling the nested controller is simply a matter of having an action with a forward that resolves to the nested controller (or a method within the controller) like this:

@Jpf.Action(forwards = { @Jpf.Forward(name = "success", path = "subscribeNewsletter.jsp")})
public Forward portlets_nestedPageFlowADone(userDataFormBean form)
{return new Forward("success");}

As noted, this takes a good deal of planning up front. For a more Agile approach, let’s look at a new approach.

Event Flows

As far as the author knows, this is the first description of using events for this particular purpose. This is probably because the author doesn’t have as much time to read articles as write them, because it is a fairly intuitive leap to go from inter-portlet communication (a common use of portal events), to passing control back and forth between specialized controllers as well as loading hidden pages used only for special purposes in a workflow.

Events are a handy way of decoupling your controllers and actions. They allow you to move from one controller to another and back again with the only explicit relationship being to the event rather than the action. If you come up with a better way of handling an event or your workflow rules change, you can simply change how the event is handled rather changing all parts of the workflow.

Let’s say we are looking at a login workflow. When the user logs in, the first step would always be to check their credentials. From that point, there are many tasks we may want the user to do. It may be time for them to change their password, or there may be a message we want to show them based on some demographic information. None of these activities are mutually exclusive and could occur in any combination. We could use simple page flows or nested page flows to achieve this, but that would require tight coupling between the actions and/or controllers. Instead, we can fire an event based on an evaluation and send the user off to take a survey (for example). When they have completed the survey we may want them to see a bulletin or not. So rather than having the logic in the survey action as to where to send them to next, we can send them back to the initial action which will then evaluate whether they should just go to the landing page or somewhere else (such as our bulletin) first. The bulletin could either send them back to the same action after the user acknowledges receipt or forward them on to the landing page itself.

Accomplishing is fairly straight forward. For each page where you want to handle an event, create a .portlet file. While the portlet configuration must have some class defined where it would presumably start, once you add event handling to the configuration you have ultimate control over how to respond to that event. Let’s look at a simple example of how this works.

public Forward begin(userFromBean form)
{
PortletBackingContext pbc = PortletBackingContext.getPortletBackingContext(getRequest());;
int status = getStatus(form.getUserId());

switch(status)
{
case 0:
pbc.fireCustomEvent("callDisplayBulletin", form);
break;
case 1:
pbc.fireCustomEvent("callChangePassword", form);
break;
case 2:
pbc.fireCustomEvent("callPresentSurvey", form);
break;
}
return new Forward("default");
}

Our logic can go in any action, but for simplicity we will put it in the begin action:Since this action method always evaluates the users’ status, we can continue to send them back here and determine where to go next. If value of the status doesn’t have a case, we simply send them to the forward destination.

Each of the events has a portlet event handler registered to listen for it. The handlers can be in as many different portlet definitions as we want, allowing for reusing the methods in the same controller on different pages or be able to have several different controllers interact with each other through the event framework. Keeping our example simple, we will have the methods in one controller in a single portlet:

<netuix:portlet definitionLabel="eventBasedPageFlow" title="Event Based Page Flow">
<netuix:handleCustomEvent event="callDisplayBulletin" eventLabel="callDisplayBulletin"
fromSelfInstanceOnly="false" onlyIfDisplayed="false" sourceDefinitionWildcard="any">
<netuix:activatePage/>
<netuix:invokePageFlowAction action="callDisplayBulletin"/>
</netuix:handleCustomEvent>
<netuix:handleCustomEvent event="callChangePassword" eventLabel="callChangePassword"
fromSelfInstanceOnly="false" onlyIfDisplayed="false" sourceDefinitionWildcard="any">
<netuix:invokePageFlowAction action="changePassword"/>
</netuix:handleCustomEvent>
<netuix:handleCustomEvent event="callPresentSurvey" eventLabel="callPresentSurvey"
fromSelfInstanceOnly="false" onlyIfDisplayed="true" sourceDefinitionWildcard="any">
<netuix:activatePage/>
<netuix:invokePageFlowAction action="presentSurvey"/>
</netuix:handleCustomEvent>
<netuix:titlebar/>
<netuix:content>
<netuix:pageflowContent contentUri="/portlets/eventBasePageFlow/EventBasePageFlowController.jpf"/>
</netuix:content>
</netuix:portlet>

The above example is for the sake or brevity. It is far more likely that these events would be handled by multiple portlets either due to presentation considerations (such as going from a page full of portlets to a page with a single portlet) or logical separation of functionality (such as a survey controller, bulletin controller, etc.).

In addition to custom events, page flow actions are events that can also be listened for, allowing for the possibility of completely changing the functionality of action by listening for it and adding additional or new behaviors. The combinations are endless and can often be changed with only a minor configuration update or a single line of code. This simplicity is key to agile methodologies and provides developers with a rapid way to add functionality on an as needed basis.
Conclusion
Workflows are a common requirement for portals. While the examples in this article revolved around a simple registration and login process, they were chosen for their commonality. Employment applications, freight logistics, legal document creation, supply requisitioning, and financial transactions are other common workflows that are often required within a portal. Those that are straight-forward with little or no deviation are easily implemented with a simple page flow. Nested page flows provide a solution to complex workflows that need to interact and provide an opportunity for the reuse of common sub-flows when a project has well defined requirements. For a more agile approach, listening for and calling events provides a flexible, loosely-coupled way to call portlet methods within and between controllers without having to know all of the specifics what future requirements may be.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson

Frankenserver: Sharing WLS Install between WLP 10.3.5 and WebCenter 11.1.1.1.7

Caveat: I have only tried this on Windows. *nix varieties may vary. For a limited time only, the latest releases of WLP and WC can run on the same version of WLS (10.3.5). I found that if you first install WLP and then install WebCenter the same Oracle Home (in my choice, C:OracleMiddleware11.1.1.7) they seem to co-exist nicely.

That said, the first time I did this I left OEPE running and tried to start JDev after applying some of the tweaks described at http://bexhuff.com/2012/09/jdeveloper-memory-and-performance. This was not a good thing, and resulted in the need for the fix described at http://ariklalo.com/2012/05/27/unable-to-create-an-instance-of-the-java-virtual-machine-jvm-dll/. I probably would not have had the problem if I had closed OEPE first, but the fix seems to be a good thing either way, so no harm done and a fairly easy lesson learned.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson

What Happens to Beehive ServiceControls in a WebLogic Portal Upgrade?

They go away. The Beehive project has been (for better or worse) retired. While some of the old Beehive classes continue to work in post 10.3.2 WLP versions, the ServiceControls do not. This is really a blessing in disguise as the old controls do not scale well.

The new approach is to use the stubs that you can generate with OEPE. The steps are here:

http://docs.oracle.com/cd/E15315_03/help/oracle.eclipse.tools.weblogic.doc/html/webservices/clientTooling.html

Facebooktwitterredditlinkedinmail
© Scott S. Nelson

Establishing WebLogic Server HTTPS Trust of IIS Using a Microsoft Local Certificate Authority

Everyone agrees that self-signed and demo certificates for SSL and HTTPS should never be used in production and preferred not to be used elsewhere.  Most self-signed and demo certificates are provided by vendors with the intention that they are used only to integrate within the same environment. In a vendor’s perfect world all application servers in a given enterprise are from the same vendor, which makes this lack of interoperability in a non-production environment an advantage. For us working in the real world, where not only do we not use a single vendor everywhere but have to make do with self-signed certificates for all but production, testing HTTPS between an IIS ASP.NET service provider and a WebLogic J2EE consumer application can be very frustrating to set up. It was for me, especially having found many blogs and discussion threads where various solutions were described but did not quite work and were all mostly similar but just a little bit different. To save both you and my future (who always seems to forget the hardest-won lessons) all of the pain and suffering, I am recording the steps that finally worked here for reference and sanity.

How You Know You Need This

The first cold clutches of dread that tells you it is going to be a long day is when you attempt to a WSDL published by IIS in WebLogic over HTTPS and you see the following:

<Jul 30, 2012 2:51:31 PM EDT> <Warning> <Security> <BEA-090477> <Certificate chain received from myserver.mydomain.com – 10.555.55.123 was not trusted causing SSL handshake failure.>

weblogic.wsee.wsdl.WsdlException: Failed to read wsdl file from url due to — javax.net.ssl.SSLKeyException: [Security:090477]Certificate chain received from myserver02.mydomain.com – 10.555.55.123 was not trusted causing SSL handshake failure.

The above is what started a three day sojourn into searching for a solution. Even people who had solved it before would tell me how they did, and then shrug when I demonstrated that the steps did not end in the success they claimed I would experience. Rather than torture you with the details of everything I did that did not work, here is what finally did work.

Export the Certificates from IE

First, take the offending WSDL URL and paste it into IE (if you have an internal Microsoft CA, you have IE, even if you don’t use it in favor of some other browser).  To state the semi-obvious, if you received the error above there is a certificate configured for the IIS host of the service and the SSL port has been configured properly. Otherwise there would be a different error, usually about the site not found or connection failed.

Once the WSDL loads, to the right of the address bar there will be a lock icon. Click the lock and then click View Certificates in the resulting dialog (if you do not have a lock icon but do have a Certificate Error message, see http://support.microsoft.com/kb/931850 for steps to install the certificate then you can continue from the point of finding the lock icon).

Figure 1: View Certificates in IE
Next, select the Details tab in the resulting dialog

 

Figure 2: Use Certificate Details to Export Certificate
Figure 2: Use Certificate Details to Export Certificate

Figure 2: Use Certificate Details to Export Certificate

Click Copy to File, then Next, then select the Base-64 encoded option for the format

 

Figure 3: Select the Base-64 encoded option for the format
Figure 3: Select the Base-64 encoded option for the format

Figure 3: Select the Base-64 encoded option for the format

For the sake of simplicity, I choose to save this to the root of the WebLogic domain. It will work from anywhere, but later you will need to type in the full path rather than just the certificate name if you save it elsewhere.

 

Figure 4: Browse to Save Location
Figure 4: Browse to Save Location

Figure 4: Browse to Save Location

 

Figure 5: Save the Certificate to the Domain Root for Convenience
Figure 5: Save the Certificate to the Domain Root for Convenience

Figure 5: Save the Certificate to the Domain Root for Convenience

This is the point where I ran into some confusion. Some articles mentioned exporting the entire chain of certificates. This supposedly works for some types of certificates, or if you have a few other tools and the time to learn them. For the SSL experts out there, they already have these tools, know how to use them well, and should not be wasting their time reading this article meant for folks who just want to get things wired up and back to unit testing and development. For the rest of us, the easiest way to make sure things will work is to just export all the links in the chain individually and let WebLogic Server worry about re-assembling them into a chain (which it does quite nicely). While perhaps not the most elegant solution, the multi-step process is easy to repeat and uses only tools that are immediately available and require no learning curve. So…

Next, go to Tools then Internet Options then the Content tab and click Certificates. Go to the Trust Root Certificate Authorities tab and find the certificate root for your Microsoft CA cert (look for the Issuer of the certificate you exported earlier).

 

Figure 6: Trusted Root Certification Authorities Tab

Figure 6: Trusted Root Certification Authorities Tab

Export this one the same way as before, with a different name

 

Figure 7: Use a Unique Name for Each Certificate
Figure 7: Use a Unique Name for Each Certificate

Figure 7: Use a Unique Name for Each Certificate

Repeat this once more for the Intermediate Certificate tab.

Import the Certificates to the WebLogic Domain

Now, open an command prompt, navigate to [WEBLOGIC_DOMAIN_ROOT]bin and execute setDomainEnv. You should then be in the root of the domain. If not, CD to the domain root.

Assuming you saved the certificate in the domain root, execute the following:

keytool -importcert -alias [ALIAS-1] -trustcacerts -file [FULL PATH TO .CER 1] -keystore truststore.jks -storepass [PASSWORD]

An example with the variables filled in is:

keytool -importcert -alias IIS-1 -trustcacerts -file microsftcert.cer -keystore truststore.jks -storepass password

After several lines out output you will be prompted with:

Trust this certificate? [no]:

The correct answer is ‘yes’ (minus the quotes, of course). You’ll you know you were successful if the response is:

Certificate was added to keystore

If not, check your typing, as that is generally the source of an error at this point.

Repeat this for all three of the certificates you exported, changing the [ALIAS-1] and [FULL PATH TO .CER 1] value each time. For example:

keytool -importcert -alias IIS-1 -trustcacerts -file microsftcert.cer -keystore truststore.jks -storepass password

keytool -importcert -alias IIS-2 -trustcacerts -file microsftcertRoot.cer -keystore truststore.jks -storepass password

keytool -importcert -alias IIS-3 -trustcacerts -file microsftcertIntermediate.cer -keystore truststore.jks -storepass password

In the above we created a new JKS key store. You can re-use an existing one by changing the name of the JKS file to one you already have and change the password to the one that matches that JKS file. For the DemoTrust.jks  that is included with WebLogic the password is DemoTrustKeyStorePassPhrase. An example here would be:

keytool -importcert -alias IIS-1 -trustcacerts -file microsoft.cer -keystore DemoTrust.jks -storepass DemoTrustKeyStorePassPhrase

keytool -importcert -alias IIS-2 -trustcacerts -file microsoftRoot.cer -keystore DemoTrust.jks -storepass DemoTrustKeyStorePassPhrase

keytool -importcert -alias IIS-2 -trustcacerts -file microsoftInter.cer -keystore DemoTrust.jks -storepass DemoTrustKeyStorePassPhrase

Whichever keystore you use, you can check your work with:

keytool -list -keystore truststore.jks -storepass password

Where “truststore.jks” and “password” can be replaced appropriately if necessary. The output will look something like this:

 

Figure 8: Output from keytool -list -keystore
Figure 8: Output from keytool -list -keystore

Figure 8: Output from keytool -list -keystore

Update the WebLogic Keystore Configuration

If you used an existing keystore rather than creating a new one, you can restart your WebLogic Server and skip the rest of this section. For those of us who created a new one because that is the instructions we found online…

Next, we need to tell WebLogic to use the JKS file (truststore.jks) we just created. Log in to the WebLogic Server Administration Console and navigate to Servers > AdminServer > Configuration > Keystores. Scroll down to “Custom Trust Keystore:” and change the value to “truststore.jks” and the value of “Custom Trust Keystore Passphrase:” and “Confirm Custom Trust Keystore Passphrase:” to the password you used when earlier, then save your changes. You will get a nice message similar to the following:

 

Figure 9: To Be Safe, Restart Anyways
Figure 9: To Be Safe, Restart Anyways

Figure 9: To Be Safe, Restart Anyways

The “No restarts are necessary” is somewhat of an exaggeration. If you want to be able to use the keystore you may need restart the server(s). To save myself aggravation, I always do. Your mileage may vary.

Conclusion

That should get you there. If there are some erroneous steps included for your situation in particular, I will offer up a semi-apology as the process described above does not take long at all and if there is one step that could be dropped from it, is still much faster than trying to figure this out from other sources.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson

The Hitchhiker’s Guide to WLP 10.3.4 Domain Creation

When creating a new WLP domain at the stage where you run the scripts to create the tables, at completion of the script run you may see:

CFGFWK-60839:  Database Load Failed!

Don’t Panic 🙂

It seems that sometimes the scripts don’t run in the proper order. Simply run them again after receiving the error and the second time should be fine.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson