4th Jan

How I Learned to Stop Worrying and Use WebSphere

Monday, January 4, 2016 - 20:34
0

Introduction

In the last few weeks I get the task to port one of our multilayered Java EE product from Wildfly 8.2 to Webpshere 8.5 (Full Profile, ND). This blog entry is about the results of my experiences with IBM WebSphere Application Server and I hope, it can be used as a quick start guide for common configuration tasks. The scope is limited to necessary information, how to get things done. If you are interested in what is under the hood, a good starting point is here.

Table of Contents

Application Architecture



Our application (wi-srv) contains five components. Components communicates each other with remote EJB calls (RMI) or messaging. Communications through messaging is implemented with Apache Camel’s JMS component. All components are able to (but not necessarily) work on separate servers. We use Spring and standard JEE features in the application, so no compatibility issues occurred in code level. You have to pay attention to class loading, because Wildfly may load many classes by default, which are not included in WebSphere.

WebSphere at first glance

In our test environment, WebSphere is installed on a GNU/Linux machine. The root installation directory is /opt/IBM/WebSphere. For further understanding, take a look at this picture:



The top logical component of the WebSphere Application Server is the cell. A cell consists of nodes and a special application, called deployment manager. The deployment manager is responsible for administering the nodes, it propagates your configuration changes or deployments. A node is a set of server instances with a node agent. The node agent monitors the server instances and routes the configuration and deployment changes. Components all so far was only for administering the server instances. The server instances (for example: invappsrv01 on the picture) run your applications, serve HTTP requests, EJB calls, etc. If you need high availability, you have to create clusters from server instances running on different nodes. If nodes are running on different machines, an outage of a node will not stop your service, the other instances of the cluster will continue to serve requests.

Let’s see, which java processes are running in our test environment:

$ ps aux|grep IBM/WebSphere
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /AppSrv02/config hu1vslpref06Cell01 hu1vslpref06Node02 invappsrv02
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /AppSrv01/config hu1vslpref06Cell01 hu1vslpref06Node01 invappsrv01
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /AppSrv01/config hu1vslpref06Cell01 hu1vslpref06Node01 invsecappsrv01
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /Dmgr01/config hu1vslpref06Cell01 hu1vslpref06CellManager01 dmgr
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /AppSrv01/config hu1vslpref06Cell01 hu1vslpref06Node01 nodeagent
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /AppSrv01/config hu1vslpref06Cell01 hu1vslpref06Node02 nodeagent
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java ... /opt/IBM/WebSphere/AppServer/profiles /AppSrv02/config hu1vslpref06Cell01 hu1vslpref06Node02 dcappsrv02
		

The node agents, server instances and the deployment manager have separate processes. The dcappsrv01 instance is stopped at the moment.

To open the WebSphere Console, use this link: https://localhost:9043/ibm/console. Another way to configure WebSphere is to use the wsadmin command line utility and its scripting features. It is important to run wsadmin with the same permissions (with the same user) as the WebSphere processes run, because you have to have write permission to configuration xmls in the AppServer directory. Here is an example, how to run wsadmin:

./wsadmin.sh -user user -password p4ss1 -lang jython

The big advantage of wsadmin implementation is the ability to use Jython as a scripting language. In Jython, you can write functions, conditional statements, etc. Almost every feature of a modern scripting language (Python) is available. Unfortunately the REPL interpreter does not support tab completion. It not even has normal backspace support! So, the preferred way is to save every successful command in a file and run wsadmin with –f switch to execute them at once.

Before we explore some necessary configuration steps in WebSphere, it is good to know: if you make a change in the configuration, you have to save it explicitly.



In Jython, you can use the AdminConfig.save() function for the same effect.

System properties

Our multilayered Java EE product (wi-srv) uses system properties to read the JNDI name of the main datasource or other environment specific settings. The first step is to set these on every server instance where needed. The breadcrumbs on the top (highlighted with yellow) helps you find the subpage where screenshot made. (Servers/All servers/…)



In Jython: 

AdminTask.setJVMSystemProperties('[-nodeName hu1vslpref06Node01 –serverName invappsrv01 -propertyName eu.dosrum.prop -propertyValue propvalue]')
AdminConfig.save()

Logging

In the filesystem, the default log files for node 1 are here: /opt/IBM/WebSphere/AppServer/ profiles/AppSrv01/logs. Here are the nodeagent log and the SystemOut logs of the server instances. WebSphere uses java.util.logging by default. If you need more sophisticated application log, I recommend to use log4j2. We use the same log4j2.xml for all instances. For this, set the log4j.configurationFile system property to the path of the lo4j2.xml file. A notable advantage of log4j2 is, that you can change the log levels or any other settings without a server or application restart.

If you use this approach, keep in mind, that messages, logged with another logger (for example errors during the deployment or internal errors of WebSphere) will be available only in SystemOut.log, and not in your application log.

Datasources

Our application uses Oracle database to store data. In Java, a standard way to communicate with the database is to use JDBC datasources. Using WebSphere, there are three steps to create a JDBC datasource:

  1. create a JDBC provider, which defines the type of the database (Oracle 10g for example)
  2. create a JAAS authentication alias for database login
  3. create the datasource.

Create a JDBC provider in Web Console: (the scope specifies, who can use the provider later. In this example, all instances of the cell can use this provider)



In Jython (the scope is the inv cluster in this example):

provider_name = 'OraXA'
target = AdminConfig.getid('/ServerCluster:inv')
template = AdminConfig.listTemplates('JDBCProvider', 'Oracle JDBC Driver (XA)')
provider_attrs = [['name', provider_name],['implementationClassName', 'oracle.jdbc.xa.client.OracleXADataSource'], ['providerType', 'Oracle JDBC Driver (XA)']]
AdminConfig.createUsingTemplate('JDBCProvider', target, provider_attrs, template)
AdminConfig.save()

Create a JAAS authentication alias in Web Console: (Security / Global security)



Creating the datasource in Web Console:



Select the created authentication alias for all drop-down.



 

Create JDBC datasource in Jython:

target = AdminConfig.getid('/ServerCluster:inv/JDBCProvider:OraXA')
jdbc_url = 'jdbc:oracle:thin:@1.2.3.4:1522:dev'
attrs = ['-name ', 'ExDS',
	   '-jndiName', 'jdbc/ExDS',
	   '-dataStoreHelperClassName', 'com.ibm.websphere.rsadapter.Oracle11gDataStoreHelper',
	   '-containerManagedPersistence', 'true',
	   '-componentManagedAuthenticationAlias', 'hu1vslpref06CellManager01/DORSUM_DCPREF',
	   '-xaRecoveryAuthAlias', 'hu1vslpref06CellManager01/DORSUM_DCPREF',
	   '-configureResourceProperties', '[[URL java.lang.String ' + jdbc_url + ']]']
AdminTask.createDatasource(target, attrs)
AdminConfig.save()
target = AdminConfig.getid('/ServerCluster:inv/JDBCProvider:OraXA/DataSource:ExDS')
AdminControl.testConnection(target)

JMS and SIB resources

We use local JMS queues for asynchronous processes in our application. WebSphere has an integrated SIB (service integration bus), which can be used for this. Creating a simple queue is more complicated than in wildfly however. Following steps are required:

  1. Create a bus (with bus security disabled)
  2. Add bus members (server instances in this case)
  3. Add bus destinations (queues)
  4. Create connection factory
  5. Create JMS queues

Create a bus (with bus security disabled) in Web Console: (Service integration / Buses)



Click on the created bus name and add bus members:



After that click Destinations to add new queues:



Add a new connection factory (use Default messaging provider, and none as authentication alias):



Add JMS queues attached to bus destinations:



 

In Jython:

AdminTask.createSIBus('[-bus exbus -busSecurity false ]')
AdminConfig.save()
log_dir_path = '/opt/IBM/WebSphere/AppServer/exbus'
AdminTask.addSIBusMember('[-bus exbus -fileStore ' + bus_member_args + ' -logDirectory ' + log_dir_path + ' -permanentStoreDirectory ' + log_dir_path + ' -temporaryStoreDirectory ' + log_dir_path + 'temp –cluster inv]')
target = AdminConfig.getid('/ServerCluster:inv')
AdminTask.createSIBJMSConnectionFactory(target, ['-name', 'QCF', '-jndiName', 'qcf/QCF', '-busName', 'exbus', '-type', 'queue'])
AdminConfig.save()
args = '[-bus exbus -type QUEUE –cluster inv -nameList [[Queue1][Queue2]]]'
AdminTask.createSIBDestinations(args)
args = ['-name', 'Queue1',
	'-jndiName', 'queue/Queue1',
	'-busName', 'exbus',
	'-queueName', 'Queue1']
target = AdminConfig.getid('/ServerCluster:inv')
AdminTask.createSIBJMSQueue(target, args)
AdminConfig.save()

Message-Driven Bean settings

If you use Message-Driven Beans in your components, unfortunately it is not enough to use the @MessageDriven annotation and @ActivationConfigProperty. Websphere needs an activation specification for each MDB. It is possible to setup activation specifications during the deployment or with an EJB deployment descriptor. Place the WebSphere-specific deployment descriptor file to META-INF/ibm-ejb-jar-bnd.xml.



The contents of ibm-ejb-jar-bnd.xml:

<ejb-jar-bnd
    xmlns="http://websphere.ibm.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-ejb-jar-bnd_1_1.xsd" version="1.1">
    
	<message-driven name="ExampleMDB"> 
		<jca-adapter activation-spec-binding-name="ExampleMDBSpec" />
	</message-driven>
</ejb-jar-bnd>

The activation-spec-binding-name refer to a saved activation specification, made in WebSphere console:



In Jython:

target = AdminConfig.getid('/ServerCluster:inv')
args = ['-name', 'ExampleMDBSpec',
	'-jndiName', 'ExampleMDBSpec',
	'-destinationJndiName', 'queue/Queue1',
	'-busName', 'exbus',
	'-destinationType', 'javax.jms.Queue']
AdminTask.createSIBJMSActivationSpec(target, args)
AdminConfig.save()

Calling stateless EJB-s from a remote server

Sometimes you need to call remote EJB from outside of the current JVM or from another application server. The tricky part here is the JNDI name of the remote interface. Normally the JNDI names looks like this: (deployment name/module name/EJB name!interface class)

java:global/wi-srv-ear/wi-srv-services-facades-impl/ClientServiceFacade!eu.dorsum.wi.services.client.facade.ClientServiceFacadeRemote

If you call remotely, you have to use this form (sever path/interface class):

cell/nodes/hu1vslpref06Node01/servers/invsecappsrv01/eu.dorsum.wi.services.client.facade.ClientServiceFacadeRemote

A full example in Java:

Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
props.put(Context.PROVIDER_URL,"corbaloc:iiop:127.0.0.1:2809");
InitialContext ic = new InitialContext(props);
Object obj = ic.lookup("cell/nodes/hu1vslpref06Node01/servers/invsecappsrv01/eu.dorsum.wi.services.client.facade.ClientServiceFacadeRemote");
//obj.method();
ic.close();

If you are calling from a non-WebSphere server, you have to add the following jars to the classpath:

  • com.ibm.ws.ejb.thinclient_8.5.0.jar
  • com.ibm.ws.orb_8.5.0.jar.

Links

To collect all the information above, I have used this pages (thanks for publishing!):

Csaba Dávid Csuzdi




Comments (0)
0