Wednesday, 12 December 2012

WLST: Simplified Server Control

WLST is a necessary evil for administering WebLogic however sometimes often the API that is exposed appears to have been produced without much regard to ease of usability. Take as an example the code required to connect to a running NodeManager in order to start/stop a Managed Server (without an Admin Server being up). WLST provides a function to connect to a NodeManager (in order to start a server) which has the form:

nmConnect(userConfigFile, userKeyFile, host, port, domainName, domainDir, nmType, verbose)

  • userConfigFile - The path to the user config file. Refer to the storeUserConfig() WLST function for further information.
  • userKeyFile - The path to the user key file. Refer to storeUserConfig() WLST function for further information. 
  • host - The listen address for the NodeManager. This is available in the domain configuration. We simply need to know which Machine definition we should reference. If we know which server we are interested in controlling then we can look up the associated Machine definition.
  • port - the listen port for the NodeManager. Again this is available in the domain configuration and again we can use a reference to a Machine definition.
  • domainName - The name for the domain. Again this is available in the domain configuration.
  • domainDir - The directory for the domain. Ok we do need this one.
  • nmType - The "type" of the NodeManager connection. Typically either "plain" or "ssl". This can be derived from the domain configuration. If a value has not been specified then the default of "ssl" should be assumed. Again we can use the reference to a Machine definition.
  • verbose -  Whether or not the NodeManager should use verbose messages. We will ignore this for our purposes and always assume a value of "false".

Therefore, assuming that we have access to the domain configuration in a given domain directory we should be able to derive many of the required parameters and simplify the nmConnect to be something a bit more user-friendly. We introduce a function nmConnectForServer which

nmConnectForServer(domainDir, serverName)

  • domainDir - The directory containing the domain configuration.
  • serverName - The name of the Managed Server whose Machine definition we want reference to determine our NodeManager connection parameters.

Once we have the WLST function we can flesh out a bit of a script that uses this function and takes appropriate arguments to allow us to control the server and view its status with the nmStart(), nmKill() and nmStatus() built-in WLST functions.

The script can then be invoked

wlst.sh nmServerControl.py start | stop | status

The advantages of this approach is that the domain configuration is used to determine the connection parameters rather then coding these parameters into a script or other configuration file. Should they change the start script will not have to be altered. Another advantage is the script becomes environment agnostic.

The following code defines such a WLST script:

 # -----------------------------------------------------------------------------  
 # Control a WebLogic server with NodeManager  
 #  
 # @arg    1    action        One of "start", "stop" or "status"  
 # @arg    2    domainDir    Path to the domain directory  
 # @arg    3    serverName    The name of the server to start  
 #  
 # -----------------------------------------------------------------------------  
 # Author:        weblogically.blogspot.com  
 # Version:        1.0a  
 # Date:            18/07/2012  
 # Tested on:    11g PS5 11.1.1.5  
 # -----------------------------------------------------------------------------  
 # This WLST script controls servers by connecting to and issuing commands  
 # directly to the NodeManager. As such the NodeManager must be running but  
 # there is no requirement for the AdminServer to be running.  
 #  
 # The builtin WLST function 'nmConnect' requires parameters that are readily  
 # available in the domain configuration. This script provides an alternative  
 # function 'nmConnectForServer' which reads the domain to locate the specific  
 # parameters values for the given domain/server and passes these to nmConnect  
 # thereby significantly reducing the number of parameters that must be  
 # manually passed in to start a server.  
 #   
 # This does function requires that the domain directory to readable.  
 #  
 # This script does not try to set the server start properties so the server  
 # should have been started via the AdminServer at least once beforehand. This  
 # should have created a server start properties file that will be then used.  
 #  
 # This script does require previously stored the security credentials in the  
 # appropriate config and key files (see storeUserConfig)  
 # -----------------------------------------------------------------------------  
 import sys  
 # @function: nmConnectForServer  
 #   
 # Connects to the NodeManager associated with the specified NodeManager  
 #   
 # @param domainDir            The path to the domain directory  
 # @param serverName            The name of the server  
 #  
 def nmConnectForServer(domainDir, serverName):  
   readDomain(domainDir)  
   domainName=get("Name")  
   cd("/Server/" + serverName)  
   machineName = get("Machine").getName()  
   cd ("/Machine/" + machineName + "/NodeManager/" + machineName)  
   nmListenAddress = get("ListenAddress")  
   nmListenPort = get("ListenPort")  
   nmType = get("NMType")  
   if nmType == None:  
     nmType = "SSL"  
   closeDomain()  
   nmConnect(  
     userConfigFile=domainDir + "/nm-userconfig.properties",  
     userKeyFile=domainDir + "/nm-userKey.properties",  
     host=nmListenAddress,  
     port=nmListenPort,  
     domainName=domainName,  
     domainDir=domainDir,  
     nmType=nmType,  
     verbose="false")  
 #  
 # @function: nmStartServer  
 #  
 # Convenience function that connects to NM and asks it to start the server  
 #  
 # @param domainDir            The path to the domain directory  
 # @param serverName            The name of the server  
 #  
 def nmStartServer(domainDir, serverName):  
   nmConnectForServer(domainDir, serverName)  
   nmStart(  
     serverName=serverName,  
     domainDir=domainDir)  
   nmDisconnect()  
 #  
 # @function: nmStopServer  
 #  
 # Convenience function that connects to NM and asks it to stop the server  
 #  
 # @param domainDir            The path to the domain directory  
 # @param serverName            The name of the server  
 #  
 def nmStopServer(domainDir, serverName):  
   nmConnectForServer(domainDir, serverName)  
   nmKill(  
     serverName=serverName)  
   nmDisconnect()  
 #  
 # @function: nmStatusServer  
 #  
 # Convenience function that connects to NM and asks it for the status of the  
 # server  
 #  
 # @param domainDir            The path to the domain directory  
 # @param serverName            The name of the server  
 #  
 def nmStatusServer(domainDir, serverName):  
   nmConnectForServer(domainDir, serverName)  
   nmServerStatus(  
     serverName=serverName)  
   nmDisconnect()  
 #  
 # @function: printUsage  
 #  
 # Print the usage pattern for this script  
 #  
 # @param domainDir            The path to the domain directory  
 # @param serverName            The name of the server  
 #  
 def printUsage():  
   print "Usage: weblogic.WLST " + sys.argv[0] + " start|stop|status <domainDir> <serverName>"  
 # hostname = java.net.InetAddress.getLocalHost().getHostName()  
 # -----------------------------------------------------------------------------  
 # MAIN  
 # -----------------------------------------------------------------------------  
 try:  
   if len(sys.argv) < 4:  
     printUsage()  
     exit("y", 1)  
   else:  
     if "start" == sys.argv[1]:  
       nmStartServer(sys.argv[2], sys.argv[3])  
     elif "stop" == sys.argv[1]:  
       nmStopServer(sys.argv[2], sys.argv[3])  
     elif "status" == sys.argv[1]:  
       nmStatusServer(sys.argv[2], sys.argv[3])  
     else:  
       print "Error: Unknown instruction"  
       printUsage()  
       exit("y", 1)  
 except:  
   print "An error occurred."  
   dumpStack()  
   raise  

Wednesday, 1 August 2012

WLST: Password Prompt

It is quite common to need to get a password from a user in a WLST script. Unfortunately WLST does not have the getpass Python package available to it. But WLST is Jython so can make use of the Java JDK classes! Java 6 introduced the Console class that has a readPassword method which would be perfect.

To get a handle on the Console object we get it from the System object:

console = System.console()

We can then invoke the readPassword method. The readPassword method takes as arguments a format string and number of var args. To simplify things we will use a format string that just outputs the first var arg as a string and then pass the prompt in as the first var arg. Jython will interpret the var args as a list so we initialize a list containing the single value which is the prompt :

passwdCharArr = console.readPassword("%s", [prompt])

The final piece of the puzzle is that the readPassword method returns char[]. We want the password as a string value. The Python String object has a join method that will work for this purpose:

passwd = "".join(passwdCharArr)

And that's it. All of this can be truncated into the following:

passwd = "".join(java.lang.System.console().readPassword("%s", [prompt]))

There may be other methods for getting a password, but this works and assuming WLST is invoked from a JDK which is at version 6 (1.6) or greater should be quite portable and not require any other Python packages.

Monday, 23 April 2012

OSB 11g: Patching OSB on Windows 7 with OPatch

There are a few hoops that need to be jumped through to patch Oracle Service Bus on Windows 7 with the OPatch utility. Note that the paths given in this post are accurate for the system on which this post was based but may be different on other systems and are dependant on where the Oracle software has been installed.

1. Start a Command window with Administrator priviledges

Administrator priviledges are required in order for the OPatch utility to get the appropriate locks on the files that are used by the OPatch utility. To initiate the Command window with Administrative priviledges right-click the icon that you use to start the Command window and select "Run as administrator" from the resulting context menu. This should launch the Command window.


2. Set the ORACLE_HOME environment variable

The ORACLE_HOME environment variable needs to be set within the context of the new Command window. This should be set to the OSB_HOME, typically this


SET ORACLE_HOME=c:\oracle\middleware\Oracle_OSB1



3. Navigate to the directory in which you have unzipped the patch to be applied.

cd PATH_TO_PATCH_DIR


4. Run the OPatch utility to apply the patch.

The OPatch utility can be used to apply the patch with the command:

opatch apply -jdk JDK_HOME -jre JRE_HOME

The OPatch utility is available from the OSB_HOME. As we have already set the ORACLE_HOME environment variable to this location we can use this variable to launch the OPatch script. You need to provide the JDK and JRE location. The exact value for JDK and JRE locations will vary depending on the system and installation locations.

%ORACLE_HOME%\OPatch\opatch apply -jdk c:\oracle\middleware\jdk160_24 -jre c:\oracle\middleware\jdk160_24\jre


5. Confirm that the patch has been applied

The OPatch utility can be used to list any patches that have been applied with the following command:

opatch lsinventory

Again, we can use the ORACLE_HOME environment variable to launch the OPatch utility

%ORACLE_HOME%\OPatch\opatch lsinventory

Possible Errors and Solutions


If you have followed the process above then you should not see these errors. They are included here for completeness.

Unable to lock Central Inventory

OiiolLogger.addFileHandler:Error while adding file handler - C:\Program Files (x
86)\Oracle\Inventory/logs\OPatch2012-04-23_11-11-28-AM.log
java.io.FileNotFoundException: C:\Program Files (x86)\Oracle\Inventory\logs\OPat
ch2012-04-23_11-11-28-AM.log (Access is denied)
Unable to lock Central Inventory.  OPatch will attempt to re-lock.
Do you want to proceed? [y|n]
n
User Responded with: N
Unable to lock Central Inventory.  Stop trying per user-request?
OPatchSession cannot load inventory for the given Oracle Home C:\oracle\middlewa
re\Oracle_OSB1. Possible causes are:
   No read or write permission to ORACLE_HOME/.patch_storage
   Central Inventory is locked by another OUI instance
   No read permission to Central Inventory
   The lock file exists in ORACLE_HOME/.patch_storage
   The Oracle Home does not exist in Central Inventory

ApplySession failed: ApplySession failed to prepare the system. Unable to lock C
entral Inventory.  Stop trying per user-request?
System intact, OPatch will not attempt to restore the system

OPatch failed with error code = 73


Solution: Run the command window with Administrator priviledges.


The Oracle Home is not OUI based home


If the ORACLE_HOME environment variable is not set then attempting to run the OPatch utility will result in the error message:

The Oracle Home is not OUI based home. Please give proper Oracle Home.

Solution: Set the ORACLE_HOME to be the OSB_HOME (Oracle_OSB1)

Thursday, 17 November 2011

Profiling Using JRockit Command Line Utility

I was recently involved with a project which was experiencing performance issues relating to high CPU utilization from WebLogic Server. Some further investigation was required in order to ascertain the code that was consuming the CPU cycles. Fortunately WebLogic was running on JRockit, unfortunately there was no fascility to run JRockit Mission Control and remotely attach to JVM and initiate an explicit JRockit Flight Recording.

The solution was to use the jrcmd utility to initiate the recording.

jrcmd start_flightrecording name=cpuprof1 filename=~/cpuprof1.jfr setting=profile duration=300s

The Oracle JRockit Flight Recorder RunTime Guide has further instructions on controlling flight recordings from the command line. The Oracle JRockit Command-Line Reference provides the complete list of options for the command-line.

Tuesday, 18 October 2011

WebLogic WS-SecureConversation with JAX-WS Example

This blog post documents the procedure for creating a Web Service and client that communicate securely via WS-SecureConversation using the WebLogic JAX-WS stack for both the client and the server. I could not find a complete example of how this should be accomplished (particularly using JAX-WS) but managed to piece a working example together from a number of other blogs and the product documentation.

The aim of this blog post is to:

  1. Provide an example of creating a JAX-WS Web Service secured with WS-SecureConversation

  2. Provide an example of creating a standalone JAX-WS Java client for invoking the Web Service

  3. Provide an example of a message exchange for WS-SecureConversation

The scope of this post has intentionally been kept to what can be done directly from WebLogic server rather than implementing what may be required for a more thorough, production style service.


Choosing the Policy For WS-SecureConversation

WebLogic ships with a number of pre-defined policies that can be used to "decorate" a web service with particular behaviours, normally related to security. For the purposes of demonstrating WS-SecureConversation we want, unsurprisingly, to use a WS-SecureConversation Policy. The pre-defined WS-SecireConversation policies are documented at:

http://download.oracle.com/docs/cd/E21764_01/web.1111/e13713/message.htm#i243913

The policy we are going to use is:

Wssp1.2-2007-Wssc1.3-Bootstrap-Wss1.1.xml.


This policy is described as:

WS-SecureConversation handshake is protected by WS-Security 1.1. The application messages are signed and encrypted with DerivedKeys. The soap:Body of the RequestSecurityToken and RequestSecurityTokenResponseCollection messages are both signed and encrypted. The WS-Addressing headers are signed. Signature and encryption use derived keys from an encrypted key.

The selected policy implements WS-SecureConversation 1.3 and WS-SecureConversation 2005/2.


By default when you deploy a web service to WebLogic that uses a WS-Trust based policy (WS-SecureConversation extends WS-Trust) WebLogic generates a co-located secure token service (STS). This STS can be used to generate a Token that can be used to access the secured web service. The Token can be used for subsequent requests.

It should be possible to use an external STS instead of the co-located STS but this is beyond the scope of this post.


Creating the Certificates and Keys for Message Level Security

The policy chosen for this example dictates encrypting and signing the RequestSecurityToken (RST) message using client/server certificates to initialize (bootstrap) the secure conversation. Therefore we need to create a pair of certifcates and keys that will be used by the server and client for encrypting and signing these messages. These certificates are only used for encrypting and signing the messages for Web Service (message-level security) and are not used for Secure Socket Layer (SSL) transport-level security.

First we create a directory to hold the certificate/key pairs that we are going to generate. This should be a well-known path as we should generate these once and may be used for other examples. As such we shall create a directory under the domain directory and will issue any further commands from that directory.

mkdir ${domain.dir}/examples/security

We then use the CertGen utility provided with WebLogic to generate the basic certificates and keys needed for this example. The CertGen utility creates certificates that are issued by the DemoCA that is (by default) trusted by the DemoTrust. This significantly simplifies the configuration that is required for this demonstration. A real-world scenario where DemoTrust is not being used will require some further configuration.

# Create the client certificate (public key) and private key pair:
java utils.CertGen -cn soademo_client -certfile soademo_client.cer -keyfile soademo_client.key -keyfilepass password

# Create the server certificate (public key) and private key pair:
java utils.CertGen -cn soademo_server -certfile soademo_server.cer -keyfile soademo_server.key -keyfilepass password

This will create a number of files.
  • soademo_client.cer.der - The client public key (certificate) in DER format
  • soademo_client.cer.pem - The client public key (certificate) in PEM format
  • soademo_client.key.der - The client private key in DER format
  • soademo_client.key.pem - The client private key in PEM format
  • soademo_server.cer.der - The server public key (certificate) in DER format
  • soademo_server.cer.pem - The server public key (certificate) in PEM format
  • soademo_server.key.der - The server private key in DER format
  • soademo_server.key.pem - The server private key in PEM format

Next we copy the DER encoded demo CA certificate from the WebLogic server installation into the example security directory. This is safer than using the certificate directy from the product installation.

copy ${wl.home}/server/lib/CertGenCA.der .

Next we convert the demo CA certifcate from binary DER format to PEM format. The PEM format is required to allow us to complete the certificate chain in the next step. PEM format files can simply be concatenated together whereas DER format cannot.

java utils.der2pem CertGenCA.der

Next we create the certificate chains for generated certificates. To achieve this the demo CA certificate (in PEM format) is concatenated to the generated certificates (also in PEM format).

type CertGenCA.pem >> soademo_client.cer.pem
type CertGenCA.pem >> soademo_server.cer.pem

Finally we create a pair of Java KeyStores (JKS) that contain the client and server public (certificate) and private key pairs:

java utils.ImportPrivateKey -keystore soademo_client.jks -storepass password -storetype JKS -keypass password -alias soademo_client -certfile soademo_client.cer.pem -keyfile soademo_client.key.pem password

java utils.ImportPrivateKey -keystore soademo_server.jks -storepass password -storetype JKS -keypass password -alias soademo_server -certfile soademo_server.cer.pem -keyfile soademo_server.key.pem password

If you dont fancy doing all that by hand then you can run the Ant script in the setup directory.

Ant Script Target:
build.cmd generateKeys


Configuring WebLogic WebServices Security

There are several configuration steps that need to be completed in order for WebLogic Server to support this example. Luckily there is a sample WLST script to do this packaged with the WebLogic Server examples. Assuming that the WebLogic Server examples were installed when WebLogic was installed then the sample WLST script can be found at:

${wl_home}/samples/server/examples/src/examples/webservices/wsrm_security/configWss_Service.py

If the WebLogic Server examples were not installed initially these can be installed be re-running the WebLogic installer and selecting the examples pack. For the purposes of providing a complete example this sample WLST script has also been packaged with the files that accompany this blog post.

Run the Ant target:
build.cmd configWss

This WLST script performs the following:

1. Enables certificate type (X.509) in the default identity asserter
2. Creates the default "default_wss" web service security context
3. Creates the Secure Conversation Token (sct) credential provider in the default wss context
4. Creates the Derived Key (dk) credential provider in the default wss context
5. Creates the Certificate (x509) credential provider in teh default wss context
6. Creates the confidentiality XML keystore (using the keystore generated earlier)
7. Creates the integrity XML keystore (using the keystore that were generated earlier)
8. Creates the X509 token handler


Building the Web Service

For this example we are going to use Oracle Enterprise Pack for Eclipse (OEPE) as the tooling for generating the web service and the client. These instructions should be easily adapted for use in JDeveloper if that is your development weapon of choice.

For this example we are going to produce the service implementation first. Ideally we should create it interface (WSDL) first but it is far easier to reference the policy using an annotation in the implementation and therefore simplifies the example.


1. Create a new Web Service Project
Name: ExampleWSSC

2. Create a new WebLogic Web Service
Source folder: ExampleWSSC/src
Package: com.oracle.uk.ocs.examples.wssc
Name: ExampleWSSC

3. Add the following code:

...
@WebService
@Policies ({
@Policy(uri = "policy:Wssp1.2-2007-Wssc1.3-Bootstrap-Wss1.1.xml")
})
public class ExampleWSSC {

@WebMethod
public String sayHello(String name) {
return "Hello " + name + "!";
}
}

The important bit here is the @Policy annotation.

4. Generate the WSDL
Parent Directory: WebContent

5. Deploy the Web Service

Check that the service has successfully deployed. Access the WSDL at the following URL:
http://localhost:7001/ExampleWSSC/ExampleWSSCService?WSDL



Building the Java Client

The client is a fairly standard JAX-WS standalone Java client. The majority of the code is concerned with setting up a Binary Security Token which requires setting up the certificate stores which will be used for the message exchange encryption.

1. Generate the Client Library

Create a new directory in the Web Service project under the WebContent folder named clientlib
Right-click the WSDL that was exported earler and select the WebLogic WebServices -> Generate Web Service Client
Location: WebContent/clientlibs

2. Create a new Java Project
Name: ExampleWSSCClient
Classpath:

3. Add the client-code
package com.oracle.uk.ocs.examples.wssc.client;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.ws.BindingProvider;

import weblogic.wsee.security.bst.ClientBSTCredentialProvider;
import weblogic.wsee.security.util.CertUtils;
import weblogic.xml.crypto.wss.WSSecurityContext;
import weblogic.xml.crypto.wss.provider.CredentialProvider;

import com.oracle.uk.ocs.examples.wssc.ExampleWSSC;
import com.oracle.uk.ocs.examples.wssc.ExampleWSSCService;

public class ExampleWSSCClient {

ExampleWSSCService service;
ExampleWSSC port;

public ExampleWSSCClient() {
System.out.println("Initialising client (service and port)");
service = new ExampleWSSCService();
port = service.getExampleWSSCPort();
}

public Map getRequestContext() {
return ((BindingProvider)port).getRequestContext();
}

public void addBinarySecureTokenCredProvider(String keystore, String keystorePassphrase, String keyAlias, String keyPassword, String serverCertFile) throws Exception{

System.out.println("Adding a BST credential provider");

List credProviders;
if (getRequestContext().containsKey(WSSecurityContext.CREDENTIAL_PROVIDER_LIST)) {
credProviders = (List) getRequestContext().get(WSSecurityContext.CREDENTIAL_PROVIDER_LIST);
} else {
credProviders = new ArrayList();
}

// Create a certificate from the PEM file
X509Certificate serverCert =
(X509Certificate)CertUtils.getCertificate(serverCertFile);
serverCert.checkValidity();

// Create the X509 Client Credential Provider
CredentialProvider cp = new ClientBSTCredentialProvider(
keystore,
keystorePassphrase,
keyAlias,
keyPassword,
"JKS",
serverCert);

credProviders.add(cp);

getRequestContext().put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credProviders);

System.out.println("Successfully added credential provider");
}

public void setEndpoint(String endpoint) {
System.out.println("Endpoint set to " + endpoint);
getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
}

public String sayHello(String name) {
System.out.println("Calling operation sayHello: name=" + name);
return port.sayHello(name);
}

public static void main(String[] args) throws Exception {

ExampleWSSCClient client = new ExampleWSSCClient();

client.addBinarySecureTokenCredProvider(
"d:/projects/oracle/osbdev/domains/osbdev/examples/security/soademo_client.jks", //keystore
"password", //keystorePassphrase
"soademo_client", //keyAlias
"password", //keyPassword,
"d:/projects/oracle/osbdev/domains/osbdev/examples/security/soademo_server.cer.pem" //serverCertFile
);

client.setEndpoint("http://localhost:7001/ExampleWSSC/ExampleWSSCService");

System.out.println("Starting...");
System.out.println(client.sayHello("Request 1"));
System.out.println(client.sayHello("Request 2"));
System.out.println("Success!");

}

}


Observing the Message Exchange

Right so we now have a service and client that are communicating. We can observe the messages going between the client and the server by introducing a proxy listening on a different port. Eclipse (OEPE) has such a proxy, simply right-click on the server and select "Monitoring -> Monitor port 7001 (http)" from the context menu. You will need to update your client endpoint to goto the proxy rather than going directly to the server.

SCT = Security Context Token (basis of a secure conversation)
RST = Request Secuirty Token
RSTR = Request Security Token Response

1. Client sends a RST/SCT request
2. Server responds with a RSTR/SCT
3. Client sends Request 1 (with the SCT from 2)
4. Server responds with the service response, secured with the derived key exchanged during 1&2
5. Client sends Request 2 (with the SCT from 2)
6. Server reponds with the service response, secured with the derived key exchanged during 1&2


Next Steps

So what can we do to build on from this? The following are a list of activities that would build on from the work performed in this blog and hopefully form the basis of some future follow-up blog posts.
  • Adapt the Wssp1.2-2007-Wssc1.3-Bootstrap-Https-UNT.xml policy to use HTTP rather than HTTPS so that the Request Security Token request can be more readily observed. This would also allow us to build a SoapUI client more easily.
  • Use a secure token service (STS) from a third-party to provide the SCT rather than the one co-located on the server.

Thursday, 29 September 2011

OSB 11g: HTTP Proxy Services And SSL

I was involved in a Oracle Sevice Bus POC recently and came across some interesting behaviour with respect to how it handles proxy services when accessed via SSL that I was not fully expecting. The proxy services behaved as expected when accessed as intended, i.e. when a proxy service that required SSL was accessed via HTTPS or a proxy service the does not require SSL was accessed via HTTP. The behaviour of interest was when the access method was the opposite of the required scheme.

The following summarizes this behaviour and discusses some options to alter the default behaviour.

SSL Proxy SSL Request Expected Actual Notes
False False 200/OK 200/OK As expected
False True 200/OK 500/ERROR Use "WLS-Proxy-SSL=true" HTTP Header to get expected result
True True 200/OK 200/OK As expected
True False 302/Redirect 302/Redirect The redirect will be to from POST to GET with missing payload!

OSB Proxy Service - HTTP Transport Without SSL

If an OSB proxy service has been configured to use the HTTP transport and has NOT been configured to "Require SSL" (transport configuration option) then attempting to access the proxy service with a client over HTTPS then the OSB will respond with a 500 error response. This is designed behaviour of OSB. It may be a requirement that an OSB proxy service should be accessible over BOTH HTTP and HTTPS. In this case the default behaviour of the OSB proxy service can be altered by setting the HTTP header "WLS-Proxy-SSL" to "true". Setting this header will allow the OSB proxy service to process the message with SSL.


OSB Proxy Service - HTTP With SSL Transport

If an OSB proxy service has been configured to use the HTTP transport and has been configured to "Require SSL" (transport configuration option) then when attempting to access the proxy service with a client over plain HTTP then OSB will respond with a 302 redirect response attempting to redirect the client to the endpoint over the HTTPS scheme. Most HTTP clients if configured to follow redirects will (correctly) follow the POST redirect GET pattern and issue a GET request to the HTTPS endpoint. The request to OSB then does not contain any payload. To protect against this there are two methods which should be considered.

If required a proxy service message pipeline can use the $inbound context variable to retrieve the HTTP method. If the HTTP verb is a GET then the proxy service can raise an error, which in turn will generate a SOAP fault. The HTTP method verb can be accessed by the value of:

$inbound/ctx:transport/ctx:request/http:http-method

The error message returned to the client can then be crafted to indicate to the client that they have attempted to use a method which is not supported and provide a hint that their web service stack may have performed the GET.

Additionally it is good practice to perform validation on the incoming request according to the OSB VETO (validate, enhance, transform, operate) pattern. This validation would fail given a missing message paylod, again by default causing a SOAP fault to be returned to the client. In this case the error message returned to the client would indicate a missing payload which may be somewhat misleading unless the client is aware that they or their web service stack performed the GET.

Monday, 22 August 2011

OSB: Avoiding Stuck Thread Warnings with JCA AQ Proxies

When you create a proxy service in OSB that uses the JCA transport (for example dequeuing from an AQ queue) then you will often see warnings in the WebLogic Server (WLS) log about stuck threads. This is because the thread that is associated with the JCA transport is continuously in a poll/sleep cycle and doesn't actually terminate. To avoid these warnings:

  1. Create a new Work Manager in the WLS console
  2. Ensure that "Ignore Stuck Threads" is checked for the new Work Manager
  3. In the Dispatch Policy for the Proxy associated with the JCA adapter select the new Work Manager

This should also have the effect of not putting the server into a Warning state.