javax.net.ssl.SSLHandshakeException: certificate expired

javax.net.ssl.SSLHandshakeException: certificate expired

javax.net.ssl.SSLHandshakeException: certificate expired. We get this exception when we try to invoke a web service from WebSphere Application Server 5.1. (In fact you will get this exception if your java version is 1.4.2_02 and earlier.) The complete stack trace of the exception is

WebServicesFault
 faultCode: Server.generalException
 faultString: javax.net.ssl.SSLHandshakeException: certificate expired
 faultActor: null
 faultDetail:
 
javax.net.ssl.SSLHandshakeException: certificate expired
        at com.ibm.ws.webservices.engine.WebServicesFault.makeFault(WebServicesFault.java:158)
        at com.ibm.ws.webservices.engine.transport.http.HTTP11Sender.invoke(HTTP11Sender.java:330)
        at com.ibm.ws.webservices.engine.transport.http.HTTPSender.invoke(HTTPSender.java:87)
        at com.ibm.ws.webservices.engine.PivotHandlerWrapper.invoke(PivotHandlerWrapper.java:212)

We get this exception because our trust store contains an expired Class 2 and Class 3 certificates. The Class 3 root certificate is used for authenticating web sites, signed code, SSL, OFX, and Administrator certificates. Class 2 root certificates are used for authenticating users and individuals.

The solution to this problem is to update the Class 2 and Class 3 certificates in our trust store. To do this either download the latest certificates from http://www.verisign.com/support/roots.html or extract the Class 2 and Class 3 certificates from a newer version of Java.

To import the latest certificates follow the below steps.

1. Delete the existing certificates by issing the following commands.
%JAVA_HOME%/bin/keytool -delete -alias verisignclass2ca -keystore %JAVA_HOME%/jre/lib/security/cacerts
%JAVA_HOME%/bin/keytool -delete -alias verisignclass3ca -keystore %JAVA_HOME%/jre/lib/security/cacerts

2. Import the new certificates by issuing the following command
%JAVA_HOME%/bin/keytool -import -v -keystore %JAVA_HOME%/jre/lib/security/cacerts -alias verisignclass2ca -file /home/Albin/certs/verisignclass2ca.arm

%JAVA_HOME%/bin/keytool -import -v -keystore %JAVA_HOME%/jre/lib/security/cacerts -alias verisignclass3ca -file /home/Albin/certs/verisignclass3ca.arm

Here verisignclass2ca.arm and verisignclass3ca.arm are the two new Class 2 and Class 3 certificates we imported. The above commands assume that your trust store is pointing to %JAVA_HOME%/jre/lib/security/cacerts, if it points to a different file, change the location.

For those who are facing difficulties in extracting the new certificates, download it from here. verisignclass2ca and verisignclass3ca. This is the certificates I have used when I encountered this issue.

Web service client with basic authentication

Configuring a web service client for basic authentication

How do we pass the username and password to a web service which is protected by basic authentication? The generated proxy client has no methods for setting the username and password.

The solution is to set the username and password inside the generated proxy client. To do this edits your proxy java file and set the username and password properties. The properties are javax.xml.rpc.Stub.USERNAME_PROPERTY and javax.xml.rpc.Stub.PASSWORD_PROPERTY. I did it inside my _initXXX method of proxy client. For eg: my code looked like the following

if (test_Request != null) {
	((javax.xml.rpc.Stub)test_Request)._setProperty(Stub.USERNAME_PROPERTY, "username");	
	((javax.xml.rpc.Stub)test_Request)._setProperty(Stub.PASSWORD_PROPERTY,"password");	
}

If you set any other property name you will get an exception

Exception in thread "main" javax.xml.rpc.JAXRPCException: WSWS3212E: Error: Property name javax.xml.rpc.Stub.XXX not supported.
	at com.ibm.ws.webservices.engine.client.Stub._setProperty(Stub.java:541)

CNTR0031W: Error starting CMP bean wcfull#WebSphereCommerceServerExtensionsData.jar

CNTR0031W: Error starting CMP bean wcfull#WebSphereCommerceServerExtensionsData.jar#XXXX: 
 javax.naming.NameNotFoundException: Context: localhost/nodes/localhost/servers/server1, name: jdbc/Default: First component in name Default not found.  Root exception is org.omg.CosNaming.NamingContextPackage.NotFound: IDL:omg.org/CosNaming/NamingContext/NotFound:1.0
	at com.ibm.ws.naming.ipcos.WsnOptimizedNamingImpl.do_resolve_complete_info(WsnOptimizedNamingImpl.java:968)
	at com.ibm.ws.naming.cosbase.WsnOptimizedNamingImplBase.resolve_complete_info(WsnOptimizedNamingImplBase.java:1431)
	at com.ibm.WsnOptimizedNaming._NamingContextStub.resolve_complete_info(Unknown Source)
	at com.ibm.ws.naming.jndicos.CNContextImpl.cosResolve(CNContextImpl.java:3493)
	at com.ibm.ws.naming.jndicos.CNContextImpl.doLookup(CNContextImpl.java:1521)
	..............................
 
JDBCPersister W CNTR0032W: Error creating CMP persister using datasource: jdbc/Default
BeanMetaData  E CNTR0035E: EJB container caught com.ibm.ejs.container.ContainerException:  nested exception is: 
	com.ibm.websphere.cpi.CPIException: Persister creation failed.The bean's J2EEName is wcfull#WebSphereCommerceServerExtensionsData.jar#XXXX. The data source name is jdbc/Default. nested exception is: 
	javax.naming.NameNotFoundException: Context: localhost/nodes/localhost/servers/server1, name: jdbc/Default: First component in name Default not found. [Root exception is org.omg.CosNaming.NamingContextPackage.NotFound: IDL:omg.org/CosNaming/NamingContext/NotFound:1.0]
	at com.ibm.ejs.container.BeanMetaData.completeInitialization(BeanMetaData.java:1299)
	at com.ibm.ws.runtime.component.EJBContainerImpl.createBeanMetaData(EJBContainerImpl.java:1039)
	at com.ibm.ws.runtime.component.EJBContainerImpl.createModuleMetaData(EJBContainerImpl.java:830)
	at com.ibm.ws.runtime.component.EJBContainerImpl.createMetaData(EJBContainerImpl.java:1575)
	at com.ibm.ws.runtime.component.MetaDataMgrImpl.createFactoryMetaData(MetaDataMgrImpl.java:115)
	......................

This was the exception I got when I tried to start my WebSphere Commerce Full Test environment after creating a new EJB. Even though the solution was very simple, for me it took some time to figure it out what exactly went wrong. If any of you facing the same problem, then here is the fix.

Open your ibm-ejb-jar-bnd.xmi file. Search for jdbc/Default. Here jdbc/Default is the name of the data source that was not found by the server. You will be able to see an entry like

	<defaultDatasource xmi:id="ResourceRefBinding_1242722240919" jndiName="jdbc/Default"/>

Just to remove the above line and your server will again start properly.

A WebSphere Process Server implementation story

A WebSphere Process Server implementation story

Just thought of writing about a WPS implementation story we completed one year back. That was the first implementation of WPS Human Task in our company. The project was a workflow system developed using the WebSphere Process Server/WebSphere Integration Developer. Unlike other projects this project’s architecture was driven by the infrastructure. This was a story of challenges.

Our application was a workflow application and we had an existing WebSphere Process Server infrastructure available. So we decided to go with WebSphere Process Server for implementing the workflow part. In addition to this WPS part we had a J2EE client for business users for accessing the human tasks. Our initial design was to host both the WPS part and J2EE part in the same WPS server (The J2EE part was very simple and contains less than 10 JSP pages only. Moreover this was an intranet application). But when we contacted the WPS hosting team, their policies does not allow us to host the J2EE application in WPS environment. Everything was looking very easy till this point. However the real challenges were on its way.

The first challenge was to separate the J2EE application from the WPS module. This was not a big deal; creating two different enterprise projects will separate out J2EE client logic from workflow part. Now the question was how does the J2EE client communicate with the WPS? Our initial idea was to use the EJB APIs for the communication. Soon we came to know that the policies will not allow us to use the EJB API and we are allowed to use only the web service APIs for connecting to WPS.

The primary requirement for generic WPS Web Service API to work was to enable the Global Security in WebSphere Application Server and in our WPS security was not enabled. No problem after some discussions with the hosting team both WPS (for hosting the workflow module) and WAS (for hosting the J2EE client) agreed to enable the global security.

So the first big challenge got resolved. Everything is set to go. Security is turned on in both WAS and WPS and we started coding. This was the time for the second road blocker. When we use the generic web service, the communication has to happen in a secured way and an SSO has to happen between WPS and WAS. In our case for login to the application we had to use a corporate authentication mechanism. Corporate provides tow types of APIs for authentication. One can be used with WAS for SSO using LtpaToken and other can be used by any application and no LtpaToken will be used. In our case we needed an SSO so the only option was to use the LtpaToken. When we asked for the LtapKey, we could see that the policy does not allow us to use the LtpaKey in our WPS and WAS servers. The people who owns the corporate authentication API was not able to share the LtpaKey with us as their policy does not allow them to share the LtpaKey to a server that is not part of their server group (They call it as UWS environment). Unfortunately both our WPS and WAS servers were not part of the server farm where our corporate authentication application is hosted. Now the first option left to us were to move both the servers under the UWS. But very soon we realized that, moving the servers to UWS will not be possible. (There was lot of issues. The team that owns these servers was not ready to move these servers to UWS. Moreover these servers were managed by two different companies). Then we decided to use the corporate authentication mechanism that does not provide us the SSO. And for the SSO part we created a TAI and installed it in WAS server. Well. SSO happened very smoothly and we were able to communicate to WPS from our WAS client using web service API.

That was not an end. We started our implementation. This time the big NO was from the database team. As part of the human workflow we had to update a database from within WPS. The workflow was actually part of a legacy application. Input for our workflow was coming from a legacy application and the output also should be for the legacy application only. But the database team was not ready to open a firewall to our servers. Soon we got a solution for this problem too. The decision was to create a text file with the entire required sql query and we will place the text file in a location. The database team will pick up the text file and execute it in the db.

Suddenly the security team came into the picture, the do not want us to do the FTP. SFTP is the only allowed protocol by them. OK. We had no other option. We started searching for a free SFTP library. (Yeah we wanted a free SFTP library because we were not in a position to tell the customer to pay some more money for a commercial SFTP library.) We could find some, but each library was giving enough troubles, some will not close the connections, some will never return a response and so on. Then we started thinking about some alternatives for SFTP library and we find that there is a tool called MoveIt available for secure file transfer. Yeah, that was the solution for all our file transfer related issues.

So all the infrastructure related issues got resolved. But by this time our design was totally changed. Lot of new systems got introduced to our application and the complexity of the design got increased too much. This leads our application to lot of new application related issues. For eg: Since the database updation was happening offline, we had to suspend our workflow till we get a response back from the database side (this also will come as a text file with .error and .success suffixes in the filename). Anyway finally we were able to complete the project successfully.

This was a good learning experience. So the moral of the story is:-

1. If you are a Project manager or an architect make sure that you understand all the policies of all the systems involved. A simple policy may screw your entire design and the cost may shoot up. When working in a big enterprise each department or each system may have different policies.

2. Talk to all the stake holders involved in the project. Otherwise you will end up in changing your design very often and all your estimates will go wrong.

3. Never ever assume things when you are in an integration project. Never your assumption will be true.

Oracle CLOB data type and Entity Beans in WebSphere Commerce Server

Oracle CLOB data type and Entity Beans in WebSphere Commerce Server.

We all know how to create an entity bean and access bean for a table that we have added newly if it contains String, Integer, Long etc data types. But when our new table has Oracle CLOB as the data type for one of the column, our access bean will not work properly. In this case in order to make our access bean working with CLOB/BLOB data type we need to edit the entity bean methods and use a session bean to store and retrieve the CLOB data. Follow the below steps to access an Oracle CLOB field from a CMP Entity bean. The trick is to use the JDBC calls to store and retrieve the CLOB data.

I am assuming that you have created a new entity bean (say NewTableBean) that has a field named ‘value’ which is of type Oracle CLOB. The data type for value field in our entity bean is a String.

1. The first step in making CLOB working for an entity bean is to create a session bean to perform our JDBC calls. When creating the session bean make sure that you have extended the session bean from com.ibm.commerce.base.helpers.BaseJDBCHelper

2. Add a method for retrieving the CLOB data using JDBC calls. The code for reading CLOB using JDBC is given below. In this case I named the method as findValueByPrimaryKey.

	public String findValueByPrimaryKey(Long primaryKey) 
			throws NamingException, SQLException {
		makeConnection();
 
		PreparedStatement ps = getPreparedStatement(
				"SELECT VALUE FROM XNEWTABLE WHERE NEWTABLE_ID = ?");
		ps.setLong(1, primaryKey.longValue());
 
		String stringTemp = null;
 
		try {
			ResultSet rs = executeQuery(ps, false);
 
			if (rs.next()) {
				Clob clobTemp = rs.getClob(1);
 
				if ((clobTemp == null) || ((int) clobTemp.length() == 0)) {
					stringTemp = null;
				} else {
					stringTemp = clobTemp.getSubString(1,
							(int) clobTemp.length());
				}
			}
		} finally {
			closeConnection();
		}
 
		return stringTemp;
	}

3. Add a method for updating the CLOB data using JDBC calls. The code for updating CLOB using JDBC is given below. In this case I have named my method as updateValueByPrimaryKey

	 public int updateValueByPrimaryKey(Long primaryKey,
		String value) throws NamingException, SQLException {
		int rowCount = -1;
		makeConnection();
 
		PreparedStatement stmt = getPreparedStatement(
				"UPDATE SET VALUE =? WHERE NEWTABLE_ID  =?");
		stmt.setLong(2, primaryKey.longValue());
 
		if (value == null) {
			stmt.setNull(1, Types.CLOB);
			rowCount = executeUpdate(stmt, false);
		} else {
			try {
				PreparedStatement stmt1 = getPreparedStatement(
						"UPDATE XNEWTABLE SET VALUE = empty_clob()  WHERE NEWTABLE_ID = ?");
				stmt1.setLong(1, primaryKey.longValue());
				stmt1.executeUpdate();
 
				PreparedStatement stmt2 = getPreparedStatement(
						"SELECT VALUE FROM XNEWTABLE WHERE NEWTABLE_ID=? FOR UPDATE");
				stmt2.setLong(1, primaryKey.longValue());
 
				ResultSet rs = stmt2.executeQuery();
 
				if (rs.next()) {
					Clob myClob = rs.getClob("VALUE");
					Writer writer = null;
					writer = ((oracle.sql.CLOB) myClob).getCharacterOutputStream();
 
					char[] aXMLDataData = value.toCharArray();
					writer.write(aXMLDataData);
					writer.flush();
					writer.close();
				} else {
					throw new ObjectNotFoundException();
				}
 
				rs.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
 
			rowCount = 1;
		}
 
		return rowCount;
	}

Now we have both the methods for updating and reading the CLOB column is ready. The next step is to call these functions from our entity bean so that the CLOB columns also will be updated/retrieved when we use our access bean.

4. Open the entity bean class we have created and search for a method called _copyFromEJB. Replace the following code

	h.put("value", getType());

with this code.

	NewTableHelperSessionBean sbNewTable = new NewTableHelperSessionBean();
 
	try {
		h.put("value",
			sbNewTable.findValueByPrimaryKey(
				newTableId));
	} catch (NamingException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	}

Here NewTableHelperSessionBean is the session bean we have created for performing our CLOB JDBC operations.

The above code will make sure that our session bean code will be called when we access the CLOB field.

5. The next step is to call the session bean update method whenever we update our entity bean. For that open the entity bean class we have created and search for a method called _copyToEJB. Replace the following code

	 if (h.containsKey("value")) {
		setValue((localValue));
	}

With this one.

	NewTableHelperSessionBean sbNewTable = new NewTableHelperSessionBean();
 
	try {
		if (h.containsKey("value")) {
			sbNewTable.updateValueByPrimaryKey(newTableId,localValue);
		}
	} catch (NamingException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	}

This will make sure that whenever the user calls the commitCopyHeler our CLOB field will be properly updated by calling the new session bean method.

We are done with all the required changes to make our entity bean working even for a CLOB column type. Now generate the deployment and RMIC code and start using your access bean/ entity bean. Have fun.