Showing posts with label WebSphere. Show all posts
Showing posts with label WebSphere. Show all posts

Tuesday, August 26, 2014

[WebSphere Portal Server] PumaSystemException on allowOperationIfReposDown="true"

When using a federated repository, let's say you're using the following snippet of ode:

try {
 PumaLocator pumaLocator = portalPumaHome.getLocator();
 PumaProfile profile = portalPumaHome.getProfile();
 List<User> users = pumaLocator.findUsersByAttribute(attribute, loginname);

 if (users != null && users.size() > 0)
 {
  // insert happy path here

 } else {
  // insert logic for "Invalid username/password" error message
  System.out.println("Invalud username/password");
 }

} catch (PumaSystemException pse) {
 // insert logic for communication exception with user repository
 System.out.println("We are currently experiencing some technical problems. Please try again later.");
 
} catch (Exception e) {
 // insert logic for other exceptions
 System.out.println("Oops, we screwed something up.");
 
}

As shown in the code snippet, you're expecting that the PumaSystemException is returned when the user repository could not be reached. This is only true when allowOperationIfReposDown is set to false. When the allowOperationIfReposDown="true", the PumaLocator operation would not return a PumaSystemException on CommunicationException with the user repository.

Therefore, your error message for this scenario will not be returned. To work your way around this, you may add an additional logic wherein you try to lookup the bind id when the user your trying to look for could not be found:


try {
 PumaLocator pumaLocator = portalPumaHome.getLocator();
 PumaProfile profile = portalPumaHome.getProfile();
 List<User> users = pumaLocator.findUsersByAttribute(attribute, loginname);

 if (users != null && users.size() > 0)
 {
  // insert happy path here

 } else {
  // adding logic to check if user repository is available
  List<User> bindusers = pumaLocator.findUsersByAttribute(attribute, bindusername);
  if(bindusers != null && bindusers.size() > 0) {
   // Invalid username/password
   System.out.println("Invalid username/password");
  } else {
   // insert logic for communication exception with user repository
   System.out.println("We are currently experiencing some technical problems. Please try again later.");
  }
 }

} catch (Exception e) {
 // insert logic for other exceptions
 System.out.println("Oops, we screwed something up.");
 
}

And that's it, hope this helps.

Friday, June 27, 2014

(J2EE) WebSphere User Registry lookup and password check

try 
{
 // Establish connection with the WebSphere user registry
 javax.naming.InitialContext initialContext = new javax.naming.InitialContext();
 com.ibm.websphere.security.UserRegistry registry = (com.ibm.websphere.security
             .UserRegistry) initialContext.lookup("UserRegistry");

 
 // Retrieves the user from the UserRegistry, if the user is not found
 // an EntryNotFoundException will be encountered
 String uniqueID = registry.getUniqueUserId(username);
 String uid = com.ibm.wsspi.security.token.WSSecurityPropagationHelper
             .getUserFromUniqueID (uniqueID);
   
 // Retrieve the securityName for the checkPassword method
 String securityName = registry.getUserSecurityName(uid);
 /*
  * Ignore warning, checkPassword returns string if securityName and 
  * password are correct otherwise, it throws a PasswordCheckFailedException
  */
 @SuppressWarnings("unused")
 String passwordCheck = registry.checkPassword(securityName, password);
   
 // IF and when no exceptions were encountered, authentication is successful
 // --- insert things-to-do-once-authenticated code here ---
catch (EntryNotFoundException enfe)
{
 // User is not in the registry
 System.out.println("Invalid username/password");
}
catch (PasswordCheckFailedException pcfe)
{
 // User is in the registry but the password is not right
 System.out.println("Invalid username/password");
}
catch (Exception e)
{
 // Catch all scenario
 System.out.println("Oh snap! Some shit just happened.");
}


(WAS) Lesson Learned: No need to restart server on application properties file update

If you have a properties file in your application which you need to update, all you need to do is STOP the application, modify the properties file, then START the application again. The properties file should now be updated.
If you have not externalized your properties file, you can access it in the profile_root/installedApps/cell_name/application_name/war_file/WEB-INF/classes. If you've place your properties file inside a package, just dig down the package path.

Friday, August 30, 2013

(BAM) Using Oracle RAC Connection String in Cognos BI DataSource

I was configuring Cognos BI Data Source for IBM Business Monitor (BAM) and followed the technote:

http://www-01.ibm.com/support/docview.wss?uid=swg21506481

I had to configure Cognos BI to connect to an Oracle RAC dataase and it says I should use the following connection string when using Service Name:

^User ID:^?Password:;LOCAL;OR;ORACLE@%s@<database_alias>/%s@COLSEQ=IBM_JD_CNX_STR:^User ID:^?Password:;LOCAL;JD-OR;URL=jdbc:oracle:thin:@//<database_host>:<database_port(e.g.1521)>/<database_service_name>;DRIVER_NAME=oracle.jdbc.driver.OracleDriver

The DBA provided me with the following Oracle RAC connection string:

(DESCRIPTION =
    (ADDRESS_LIST =
      (LOAD_BALANCE = yes)
      (ADDRESS = (PROTOCOL = TCP)(HOST = ORADB01.mydomain.com)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = ORADB02.mydomain.com)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = ORADB.mydomain.com)
      (FAILOVER_MODE =
        (TYPE = SELECT)
        (METHOD = BASIC)
        (RETRIES = 180)
        (DELAY = 5)
      )
    )
  )

I was trying to figure out what <database_alias> means until it hit me and realized the answer is the alias I used in the TNSNAMES.ORA for the oracle client (instant client) I installed previously.

TNSNAMES.ORA

PROD_ORADB = (DESCRIPTION =
    (ADDRESS_LIST =
      (LOAD_BALANCE = yes)
      (ADDRESS = (PROTOCOL = TCP)(HOST = ORADB01.mydomain.com)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = ORADB02.mydomain.com)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = ORADB.mydomain.com)
      (FAILOVER_MODE =
        (TYPE = SELECT)
        (METHOD = BASIC)
        (RETRIES = 180)
        (DELAY = 5)
      )
    )
  )


After setting PROD_ORADB to the <database_alias> parameter, it worked perfectly:

^User ID:^?Password:;LOCAL;OR;ORACLE@%s@PROD_ORADB/%s@COLSEQ=IBM_JD_CNX_STR:^User ID:^?Password:;LOCAL;JD-OR;URL=jdbc:oracle:thin:@(DESCRIPTION = (ADDRESS_LIST = (LOAD_BALANCE = yes) (ADDRESS = (PROTOCOL = TCP)(HOST = ORADB01.mydomain.com)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = ORADB02.mydomain.com)(PORT = 1521)))(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = ORADB.mydomain.com) (FAILOVER_MODE = (TYPE = SELECT) (METHOD = BASIC) (RETRIES = 180) (DELAY = 5))));DRIVER_NAME=oracle.jdbc.driver.OracleDriver


Thursday, June 20, 2013

DataPower: Setting the Content-Type header in an Error Rule

I learned a new trick today. This has been haunting me for many months now but I've managed to avoid implementing it for my customer. Today, being pressured by deadlines, I learned a cool new trick with DataPower: setting the Content-Type in an error rule.

The reason why this has eluded me for sometime is that no matter how much effort I do setting it inside an XSL in a transform action using either <dp:set-http-response-header name="'Content-Type'" value="'application/json'" /> or <dp:set-response-header name="'Content-Type'" value="'application/json'" /> and even if I do a <dp:freeze-headers /> after that, it still didn't work. I've also tried doing a set-var action setting "var://service/set-response-header/content-type" to application/json and placing it before the result action, it still does not work (this was what most of my Google search said to do). And I've been trying and trying every now and then to make it work but to no avail.

Finally, this afternoon, pressured to meet a tight deadline, I did one final Google search and found a link that I haven't read before simply because it did not exist prior to June 19, 2013. I tried it out and it worked perfectly!

How it was done was quite unusual actually, I got the part where you specify the value of var://service/set-response-header/content-type right but what I did wrong was were I placed it. Prior to June 19, my quick searches say that the set var action should be placed before the result action. But the link to a developerWorks forum post says that it should be set AFTER the result action.



When I tried it out for myself, I jumped in complete joy (the geek side of me kicking-in) and amazement. I was like: WOW! Finally!

Thursday, June 6, 2013

Date Comparison using XSLT and XPath in DataPower

It was a brain-draining day at work. I had to implement a customized SLM policy enforcement for a customer where we pull a meta-data of policies from an xml and enforce it inside an XSL transform action. The trickiest part of the day was implementing the Allowed Time of Day policy. From the parameters defined in the xml snippet we had to reject transaction if the current time is beyond the start and stop times. This is the element that we are retrieving the start and stop time from:
<Daily StartTime="08:00:00+08:00" StopTime="17:00:00+08:00"/>

After hours of attempts, below is the solution I've came up with.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
 version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
 xmlns:dp="http://www.datapower.com/extensions" 
 xmlns:func="http://exslt.org/functions" 
 xmlns:df="http://lmls.ph/date_func" 
 xmlns:date="http://exslt.org/dates-and-times"
 extension-element-prefixes="dp slm func" 
 exclude-result-prefixes="dp slm func">
 
 <!-- removes the padded zero in front of HH or mm or ss -->
 <func:function name="df:removeFrontPaddedZeroes">
  <xsl:param name="pad_num" />
  <xsl:if test="starts-with($pad_num, '0')">
   <func:result select="substring($pad_num,2)" />
  </xsl:if>
  <func:result select="substring($pad_num,1)" />
 </func:function>
 
 <!-- converts the time format 00:00:00+00:00 into numbers for comparison -->
 <func:function name="df:convertTimeToNumber">
  <xsl:param name="zTime" />
  <xsl:if test="substring($zTime,9,1) = '+'">   
   <func:result select="((number(df:removeFrontPaddedZeroes(substring(string($zTime),1,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),4,2))) * 100) + number(df:removeFrontPaddedZeroes(substring(string($zTime),7,2)))) + ((number(df:removeFrontPaddedZeroes(substring(string($zTime),10,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),13,2))) * 100))" />   
  </xsl:if>
  <func:result select="((number(df:removeFrontPaddedZeroes(substring(string($zTime),1,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),4,2))) * 100) + number(df:removeFrontPaddedZeroes(substring(string($zTime),7,2)))) - ((number(df:removeFrontPaddedZeroes(substring(string($zTime),10,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),13,2))) * 100))" />  
 </func:function>

 <!-- function that evaluates if the current time is within the start and stop time bounds -->
 <func:function name="df:testTimeOfDay">
  <xsl:param name="start_time" />
  <xsl:param name="stop_time" />  
  <xsl:variable name="current_time" select="date:time()" />

  <xsl:variable name="start_time_c" select="df:convertTimeToNumber($start_time)" />
  <xsl:variable name="stop_time_c" select="df:convertTimeToNumber($stop_time)" />
  <xsl:variable name="current_time_c" select="df:convertTimeToNumber($current_time)" />

  <xsl:if test="(number($current_time_c) &gt;= number($start_time_c)) and (number($current_time_c) &lt;= number($stop_time_c))">
   <func:result select="true()" />
  </xsl:if>
  <func:result select="false()" />
 </func:function>
<xsl:stylesheet>

There are three functions in this XSL namely: df:testTimeOfDay, df:converTimeToNumber, and df:removeFrontPaddedZeroes the comments explain their purposes. The converTimeToNumber is where bulk of the logic goes and before I arrived to this code I had the split it up into smaller and more easy to understand pieces:
# The input parameter is called zTime, this comes in the format of 00:00:00+00:00
zTime = "00:00:00+08:00"

# Now in the format HH:mm:ss[\-\+]Hz:mz
HH = number(df:removeFrontPaddedZeroes(substring(string($zTime),1,2))) 
mm = number(df:removeFrontPaddedZeroes(substring(string($zTime),4,2)))
ss = number(df:removeFrontPaddedZeroes(substring(string($zTime),7,2)))
op = substring(string($zTime),9,1)
Hz = number(df:removeFrontPaddedZeroes(substring(string($zTime),10,2)))
mz = number(df:removeFrontPaddedZeroes(substring(string($zTime),13,2)))

# Then logically we get HHmmss by doing (HH * 10000) + (mm *100) + (ss)
# And, Hzmz by doing (Hz * 10000) + (mz * 100)
HHmmss = (number(df:removeFrontPaddedZeroes(substring(string($zTime),1,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),4,2))) * 100) + number(df:removeFrontPaddedZeroes(substring(string($zTime),7,2)))
Hzmz = (number(df:removeFrontPaddedZeroes(substring(string($zTime),10,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),13,2))) * 100)

# Then our if condition should be which provides the final output in HHmmss + or - Hzmz
if op = '+'
 result = ((number(df:removeFrontPaddedZeroes(substring(string($zTime),1,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),4,2))) * 100) + number(df:removeFrontPaddedZeroes(substring(string($zTime),7,2)))) + ((number(df:removeFrontPaddedZeroes(substring(string($zTime),10,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),13,2))) * 100))
else 
 result = ((number(df:removeFrontPaddedZeroes(substring(string($zTime),1,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),4,2))) * 100) + number(df:removeFrontPaddedZeroes(substring(string($zTime),7,2)))) - ((number(df:removeFrontPaddedZeroes(substring(string($zTime),10,2))) * 10000) + (number(df:removeFrontPaddedZeroes(substring(string($zTime),13,2))) * 100))

Finally we can call the the df:testTimeOfDay to return true() when current time when current time is greater than or equal to start time AND current time less than or equal to stop time. See snippet below:
<xsl:variable name="start_time" select="$sla_snippet/Daily/@StartTime" />
<xsl:variable name="stop_time" select="$sla_snippet/Daily/@StopTime" />

<xsl:if test="df:testTimeOfDay($start_time,$stop_time) = false()">
 <dp:xreject reason="'Time of Day policy violated.'"/>
</xsl:if>

I haven't really done much testing and error handlings with this but I was able to test it making happy paths. This at least gives an idea of how you can perform comparison between dates.

Wednesday, September 26, 2012

Serving static files using DataPower

I was customizing Login and Request Permission forms for implementing OAuth with DataPower when I encountered a road-block where I need to host my CSS, JPEG, and other Web Resource files. The first solution that came to mind is to use an HTTP Server, but, since the current architecture of our client does not have an HTTP Server in the DMZ along with DataPower, I needed to find a way to host the files in DataPower.

A few Google searches later and I found this blog post from HermannSW where it says: define a local HTTP service listening on 127.0.0.1:8888 with base directory "logtemp:///". I figured that this could probably apply to my current situation.

First thing I did was to define a local:/// sub-directory to contain all my files:


Then, I created an HTTP Service with base directory pointed at "local:///WEB-CONTENT".

**Remember to assign the correct Local IP Address (or Alias) and Port Number to fit your configuration.

After defining the HTTP Service, I can now access the files via http://local_ip_address:port_number/full_path/file_name. I then defined my custom Login and Request Permission forms to use the files hosted in the HTTP Service.

Wednesday, August 15, 2012

Work Around: Copying Files from a Windows Local Machine to DataPower via CLI

I encountered a situation with DataPower where we can't access the WebGUI and the quickest way to fix it was to reinitialize the box and restore from backup. To be able to do this, I needed to copy a firmware image into the box via CLI, execute reinit <image_file_name>, reconfigure the appliance, and then restore from backup.

I had a problem copying the firmware image from one of the RHEL servers to DataPower and found out that there is a known issue when copying via SCP. Because of this, I thought of placing the firmware image inside an HTTP server. Then, instead of using SCP/SFTP, I copied using HTTP protocol.

1. Install WAMPServer or any HTTP server that your familiar with.
2. Configured the httpd.conf to grant all access to the www folder where I placed the firmware image (do something similar to your choice of HTTP Server)
3. Restarted the WAMPServer
4. The file should be located under http://{YOUR_IP}/{FILENAME}
5. Run the following commands via SSH in DataPower

test tcp-connection {YOUR_IP} 80 //make sure DataPower can communicate with your machine
config
copy http://{YOUR_IP}/{FILENAME}  image:///{FILENAME}

After copying the firmware image, I just followed the guide for resetting DataPower. As long as you have a RJ45 to USB cable, it should be easy-as-pie to reinitialize.

Sunday, July 22, 2012

Installing IBM BPM V 8.0 in an unsupported OS


It is NOT recommended that you install any IBM software in a non-supported OS. If you encounter any problem with the software you will not be able to reach IBM Support for help unless you install it in a supported OS.

Before version 8, the way to do this was to add -OPT disableOSPrereqChecking="true" to the response file.  In version 8, we still have the same property but it is placed in a different location. To disable OS prerequisite checking in version 8:

  1. If Installation Manager is already installed do (a) else do (b)
    1. Go to IM_install_root/eclipse/configuration
    2. Go to Installer_extract_path/IM/configuration
  1. Modify config.ini
  2. Append in the file: disableOSPrereqChecking=true
  1. Save
  2. Restart your installation

Tuesday, July 17, 2012

DataPower Version Upgrade from 4.0.2 to 5.0.0

Yesterday, I had the opportunity to upgrade a DataPower XB62 appliance from version 4.0.2 to version 5. I followed the steps specified in the product documentation and simplified it:


  1. Download the images in Fix Central
  1. ENSURE that you have  a working privileged user ("admin") and that you have access to it (meaning the password you're using is recent)
  1. Login as admin in default domain
  1. Backup the appliance's configuration
    1. Export Configuration
    1. Select Create a backup of the entire system 

  1. Backup files from local:/// on all domains
    1. Login to application domain
    1. Export configuration
    2. Check "Export all local files" option
    3. Download
    4. Repeat step 3.a for other domains
    5. Delete all files in local:///  on all domains to free up space (do this if there's little space left in your appliance)
    1. Save Configuration
  1. Disable application domains
    1. Configuration > Application Domain
    1. Disable domains other than the default domain
    2. Save configuration
  1. Reboot to free up resources
    1. System Control
    2. Set mode to Reboot System
    3. Click shutdown button
  1. Wait for reboot
  1. Upload the firmware image
    1. System Control
    2. Boot Image > Upload or Fetch
  1. Verify  the firmware upgrade version
  1. Enable Application Domains and Restore the local:/// files you backed up if there were any.

Well, that's about it. This is just a short version of the procedure I recommend that you still checkout the product documentation.

Friday, July 13, 2012

Installing BPM 8 Process Center Advanced on RHEL 5 Silently

IBM recently released BPM version 8.0. In line with this new version, I had the opportunity to install BPM 8 Process Center Advanced on a Red Hat Enterprise Linux machine. To start off, there were really no difference in the installation procedure from the previous version (BPM 7.5). The information center provides a more detailed procedure on how to do this but here's how I installed it. Note that this is just base installation and I haven't created the profiles yet. I'll write about profile creation later on.

Pre-installation Steps:

1. Login as root
2.  Use a text editor or vi to modify /etc/security/limits.conf and insert/modify the nofile parameter
* hard nofile 8800
* soft nofile 8800

3. Set umask value
umask 022

4. Restart the server
init 6

5. Transfer BPM images to the server. I used the following directory for this example:
/opt/BPM8

6. Extract the BPM images to a directory. I ran the following commands to extract the images to /opt/installers/bpm
cd /opt/installers/bpm
tar -zxvf /opt/BPM8/BPM_Adv_V800_Linux_x86_1_of_3.tar.gz
tar -zxvf /opt/BPM8/BPM_Adv_V800_Linux_x86_2_of_3.tar.gz
tar -zxvf /opt/BPM8/BPM_Adv_V800_Linux_x86_3_of_3.tar.gz

Once the images are extracted you can proceed in creating your response file. Create a copy of the file /opt/installers/bpm/responsefiles/BPM/template_response.xml. Based on that file, specify the following fragments:

I modified the repository location from a relative path to the full path of the BPM installers. I also change repos_32bit to repos_64bit since I was installing on a 64bit machine.
<server>
 <repository location='/opt/installers/bpm/IM/' temporary='true'/>
 <repository location="/opt/installers/bpm/repository/repos_64bit/" />
</server>
I specified the Installation Manager location to /apps/IBM/InstallationManager
<profile kind='self' installLocation='/apps/IBM/InstallationManager' id='IBM Installation Manager'>
 <data key='eclipseLocation' value='/apps/IBM/InstallationManager'/>
 <data key="cic.selector.nl" value="en" />
</profile>
I specified the BPM installation location to /apps/IBM.WebSphere/ProcServer. I also removed the DB2 Express element because I didn't want to install the DB2 9.7 Express. Change 32bit to 64bit.
<profile installLocation='/apps/IBM/WebSphere/ProcServer' id='IBM Business Process Manager Advanced'>
 <data key='eclipseLocation' value='/apps/IBM/WebSphere/ProcServer' />
 .
 .
 <data key='user.select.64bit.image,com.ibm.websphere.ND.v80' value='true'/>
</profile>
I removed the DB2 Express element, used the 64bit offering: com.ibm.sdk.6_64bit, and specified "wps.client.feature,wps.server.feature,bpmAdv.nonprod" as features to install. Note that bpmAdv.nonprod is used for development and test environment. Then, bpmAdv.prod is used for production environment.
<install>
 <offering profile="IBM Business Process Manager Advanced" id="com.ibm.websphere.ND.v80" features='core.feature,ejbdeploy,thinclient,embeddablecontainer,samples,com.ibm.sdk.6_64bit'/>  
 <offering profile="IBM Business Process Manager Advanced" id="com.ibm.bpm.ADV.V80" features='wps.client.feature,wps.server.feature,bpmAdv.nonprod'/> 
</install>
I also specified the eclipse cache location as such:
<preference value="/apps/IBM/SDPShared" name="com.ibm.cic.common.core.preferences.eclipseCache" />
Once I was done modifying the response file template, I saved it under /opt/installers/responsefiles/ as install_response.xml After saving the response file, I executed the following command:
/opt/installers/bpm/IM/installc -acceptLicense input /opt/installers/responsefiles/install_response.xml -log /opt/installers/logs/silent_install.log
Note, that all the above steps are done using the root user. You may want to assign a user permissions to the directories that were used.

For a more more information on installing BPM 8.0, checkout the following links:

Thursday, July 12, 2012

Modifying Endpoint Bindings Using XML Property File in BPM 7.5


In my  previous post, Deployment Script for BPM 7.5 on a Linux machine, I mentioned that I would post another blog about modifying endpoint bindings of your SCA imports. During that time we used a properties file to specify deployment specific information such as installation directory, applications to install, list of federated nodes,  etc. When I was working with WPS 6.2 in my previous project, I used the same properties file to specify the SCA import bindings to modify. To tell you the truth, the property was not very readable and it got large to read at some point. I specified the import bindings this way:

# modify_ws_binding="<module_name>::<import_name> <endpoint_binding>,<import_name> <endpoint_binding>,<import_name> <endpoint_binding>;;

Realizing that problem, I had the idea to use XML as "response" file for modifying the endpoint bindings via wsadmin. The structure goes something like:

<?xml version="1.0" encoding="UTF-8"?>
<deployment>
 <module name="ModuleName">
  <import name="ImportName1" type="WS">
   <property name="endpoint" value="http://localhost:9800/setWhatever" />
  </import>
  <import name="ImportName2" type="WS">
   <property name="endpoint" value="http://localhost:9800/setWhatever" />
  </import>  
  <import name="ImportName3" type="HTTP" level="methodName">
   <property name="endpointURL" value="http://localhost:9800/setWhatever" />
   <property name="endpointHttpMethod" value="POST" />
   <property name="httpProxyPort" value="80" />
  </import>
  <import name="ImportName3" type="HTTP" level="methodName1">
   <property name="endpointURL" value="http://localhost:9800/getWhatever" />
   <property name="endpointHttpMethod" value="POST" />
   <property name="httpProxyPort" value="80" />
  </import>
 </module>
 <module name="ModuleName1">
  <import name="ImportName1" type="WS">
   <property name="endpoint" value="http://localhost:9800/setWhatever" />
  </import>
  <import name="ImportName2" type="WS">
   <property name="endpoint" value="http://localhost:9800/setWhatever" />
  </import>  
  <import name="ImportName3" type="HTTP" level="ImportName3">
   <property name="endpointURL" value="http://localhost:9800/setWhatever" />
   <property name="endpointHttpMethod" value="POST" />
   <property name="httpProxyPort" value="80" />
  </import>
 </module>
</deployment>

The root element is the deployment element. I used this term because I have hopes of creating a deployment script using XML. Now, the deployment element has a an array of children named module. Each module tag has an attribute name where you specify the SCA module name. module has an array of children named import. Each import element has an attribute name  which specifies the name of the Import binding and an attribute type which specifies the type of SCA import binding (for now, the script only supports web services and http bindings).  The attribute level is an attribute for HTTP bindings. If you want to specify values on the import level, you specify the name of the import. Otherwise, specify the method name. Lastly, the import element has multiple property elements. Each property element has an attribute name which specifies the name of the parameter and an attribute value which specifies the value of the parameter. Visit the infocenter to get a list of parameters and values you can set for methods modifySCAImportHttpBinding and modifySCAImportWSBinding.

After writing the xml file, I wrote the code for reading it. I don't want to explain in detail the code, but, in general, what it does is, it parses the xml file. From the root element, it traverses down to the properties element and executes modifySCAImportHttpBinding  or modifySCAImportWSBinding using the values specified in the xml file. Lastly, it saves the configuration. In a network deployment, you might want to add the synchronize nodes code block that I used in my previous post.

Here's the code:

import javax.xml.parsers.DocumentBuilderFactory as DocumentBuilderFactory
import javax.xml.parsers.DocumentBuilder as DocumentBuilder
import sys

dbf = DocumentBuilderFactory.newInstance()
db = dbf.newDocumentBuilder()
dom = db.parse(sys.argv[0])

deployment = dom.getDocumentElement()
modules = deployment.getElementsByTagName('module') 

m=0
while m<modules.getLength():
 module = modules.item(m)
 moduleName = module.getAttribute('name')
 scaImports = module.getElementsByTagName('import')
 i=0
 while i<scaImports.getLength():
  scaImport = scaImports.item(i)
  scaImportType = scaImport.getAttribute('type')
  scaImportName = scaImport.getAttribute('name')
  properties = scaImport.getElementsByTagName('property')
  p=0
  props = ''
  levelBegin = '<' + scaImport.getAttribute('level') + '>'
  levelEnd = '</' + scaImport.getAttribute('level') + '>'
  if (scaImportType == 'WS'):
   while p<properties.getLength():
    property = properties.item(p)
    props += ' -' + property.getAttribute('name') + ' ' + property.getAttribute('value')
    p+=1
   #endWhile
   try:
    print 'AdminTask.modifySCAImportWSBinding(\'[-moduleName ' + moduleName + ' -import ' + scaImportName + props + ']\')'
    AdminTask.modifySCAImportWSBinding('[-moduleName ' + moduleName + ' -import ' + scaImportName + props + ']')
   except :
    print "Unexpected error:", sys.exc_info()[0]
  elif (scaImportType == 'HTTP'):
   while p<properties.getLength():
    property = properties.item(p)
    props += ' -' + property.getAttribute('name') + ' ' + levelBegin + property.getAttribute('value') + levelEnd
    p+=1
   #endWhile
   try:
    print 'AdminTask.modifySCAImportHttpBinding(\'[-moduleName ' + moduleName + ' -import ' + scaImportName + props + ']\')'
    AdminTask.modifySCAImportHttpBinding('[-moduleName ' + moduleName + ' -import ' + scaImportName + props + ']')
   except :
    print "Unexpected error:", sys.exc_info()[0]
  i+=1
 m+=1
else:
 print 'Processing Complete...'

print 'Saving...'
AdminConfig.save()

We then execute this using wsadmin:

wsadmin -lang jython -f script_path\modify_ws_bindings.py xml_path\properties.xml

Note that there are not much error checking done with this code. If you encounter any error don't forget to write a comment. 

References:
Changing an import binding using commands


Tuesday, June 19, 2012

Configure WebSphere DataPower SQL Data Source with Oracle Real Application Clusters (RAC)

I've been banging my head lately trying to figure out how I would configure an SQL Data Source for DataPower for this Oracle connection string:

MYDB =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (LOAD_BALANCE = yes)
      (ADDRESS = (PROTOCOL = TCP)(HOST = MYDB01)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = MYDB02)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = mydb.mycompany.com.ph)
      (FAILOVER_MODE =
        (TYPE = SELECT)
        (METHOD = BASIC)
        (RETRIES = 180)
        (DELAY = 5)
      )
    )
  )

Since I don't know anything about Oracle, I have no idea how to use this connection string for DataPower. I do know, however, how to configure an SQL Data Source given an Oracle SID at a single database host. But, since the connection string given to me is a "load balanced" one, I didn't know anymore how to configure a data source for that.

I did a little research and found the following;

I overlooked them quite many times simply because I do not know what Oracle Real Application Clusters mean. I did some reading on them but I wasn't sure if it was what the client is using. I did more readings until finally, I had to ask the only question left unanswered: "Are we using Oracle Real Application Clusters for this?". And, when the answer was a definitive 'YES', I knew by then that the solution was just under my nose.

I followed the steps mentioned in the first link but after saving my configuration my log messages indicate:

Connection error message: Unicode converter truncated character


I then checked the second link at pages 10-11 and noticed that the AlternateServers value here was enclosed in parenthesis. I tried that again and, voila, it worked!

Here's my final configuration:



This situation reminded me of what one of our IT Architects told me when I was still very new in IBM:
Never hesitate to ask questions, it will save you a lot of time
 Now, I have something to add to that saying:
Ask the right question and you'll get the answer you seek.

Thursday, June 14, 2012

Deployment Script for BPM 7.5 on a Linux machine

In my experience in developing applications for WebSphere Process Server (or now the Business Process Manager), it is quite tedious to uninstall, install, and start your applications using the Administrative Console and the Business Process Choreographer application. And at development phase, you will do this almost on a daily basis; deploying EAR files into your Development and probably even the Test environment. To make this task easier to do, I usually use a script to do this for me. So that, with just one command I can sit back, relax, and wait for the deployment to finish.

NOTE: This is not advisable to do in a Production environment since the uninstall part will force delete all process instances (even the running ones).

In using the following set of scripts the following are assumed:
  • The installation and profile directory are standardized across the physical machines where the BPM nodes are residing
  • It is assumed that the applications are deployed in the AppTarget cluster
  • A installableApp directory is created and the EAR files to install are located there
The first script is your shell script for the Linux server. This script will force uninstall existing applications using the bpcTemplates.jacl. Once all the applications specified in your properties file are uninstalled, it will then run the install.py jython script with your wsadmin. You should place this file in the same directory as the two other scripts. Note that I didn't use file checking to verify if the files exist.

Filename: deploy.sh
#!/bin/ksh

# --- MODIFY --- #
install_root=/opt/IBM/WebSphere/ProcServer
profile_root=/opt/IBM/profiles/BPMDmgr
deployer_root=/opt/IBM/deployer

# --- DO NOT MODIFY --- #
wsadmin=$profile_root/bin/wsadmin.sh
bpc_templates=$install_root/ProcessChoreographer/admin/bpcTemplates.jacl
install_py=$deployer_root/install.py
deployment_props=$deployer_root/deployment.properties

# NO CHECKING WILL BE PERFORMED IF THE ABOVE FILES EXIST. IT IS ASSUMED THAT THEY DO. #

. $deployment_props
IFS=","
set -A installable_app $installable_apps

# UNINSTALL APPS
count=${#installable_app[@]}
for app in ${installable_app[@]}
do
 echo "INFO: Uninstall enterprise application: $app" 
 if [ $1 ] && [ $2 ]
 then
  $wsadmin -f $bpc_templates -uninstall $app -force -username $1 -password $2
 else
  $wsadmin -f $bpc_templates -uninstall $app -force
 fi
done

# INSTALL AND START APPS
echo "INFO: Install and start application/s: $installable_apps"
if [ $1 ] && [ $2 ] 
then
 $wsadmin -lang jython -f $install_py $deployment_props -username $1 -password $2
else
 $wsadmin -lang jython -f $install_py $deployment_props
fi

# --- END CODE --- #


The second script is the jython script that wsadmin will use to install and start the applications. Using the properties file, it will identify what applications to install into which cluster. This script also saves and synchronizes the changes to all the nodes specified in your properties file.

Filename: install.py

import java.util as util
import java.io as javaio
import java.lang.System as javasys
import time
import sys

# LOAD properties file
properties = util.Properties()
propertiesfis = javaio.FileInputStream(sys.argv[0])
properties.load(propertiesfis)

nodeNames=properties.getProperty('node_names').split(',')
installableApps=properties.getProperty('installable_apps').split(',')
installationDir=properties.getProperty('installableApps_dir')
cluster=properties.getProperty('apptarget_cluster')
dotExtension='.ear'

# Install applications
for installableApp in installableApps:
 print 'Installing ' + installableApp + '...'
 AdminApp.install(installationDir + installableApp + dotExtension, ['-appname', installableApp, '-cluster', cluster])
#endFor

AdminConfig.save()

# Synchronize nodes
for nodeName in nodeNames:
 print 'Sync ' + nodeName + ' node...'
 AdminControl.invoke(nodeName, 'sync')
#endFor

# ---
# You can insert other steps here like import binding and security role re-map 
# If you do add steps here, make sure you save and synchronize
# ---

# Start applications
lineSeparator = sys.getProperty('line.separator')
members = AdminConfig.getid('/ServerCluster:' + cluster +'/ClusterMember:/').split(lineSeparator)
for member in members:
 serverName = AdminConfig.showAttribute(member, 'memberName')
 appManager = AdminControl.queryNames('process='+serverName+',type=ApplicationManager,mbeanIdentifier=ApplicationManager,*').split(lineSeparator)
 for mgr in appManager:
  if (mgr != ''):
   for installableApp in installableApps:
    print 'Starting application ' + installableApp + '...'
    AdminControl.invoke(mgr, 'startApplication', installableApp)
   #endFor
  #endIf
 #endFor
#endFor


The last file is the properties file used in the shell script as well as in the jython script. This is where you specify the installableApps directory, the applications to be deployed, the name of the nodes, and the name of the apptarget cluster.

Filename: deployment.properties


# Directory where your apps to be installed are located
installableApps_dir=/opt/IBM/installableApps/

# installable_apps=,, ...
installable_apps=BalanceInquiryApp,ChargingApp

# Node names (List down all federated nodes)
node_names=BPMNode01,BPMNode02

# AppTarget cluster
apptarget_cluster=SOA.AppTarget

# You can add aditional properties like endpoint binding properties or security role mapping

Now, finally, you need to execute deploy.sh in your Linux terminal. What we're doing here is just a basic uninstall, install, and start of applications. In some cases, you may wish to modify the endpoint bindings of imports (I guess I can post another blog for this) or remap the security roles per application.

Lastly, I haven't really tested the above scripts. I have a working script that I actually use and but, of course, I can't post it here. If you encounter any problem with the script, just let me know!

Happy Coding!

Wednesday, June 13, 2012

Retrieving the original Content-Type in WebSphere DataPower response Processing Rule

I ran into a situation with DataPower today where the Content-Type of the responses from DataPower is altered from the original Content-Type of the backend service into text/plain. I have a Multi-Protocol Gateway with an HTTP front-side handler and configured to have a Non-XML request and response type. I have multiple processing policies for accepting and sending requests to a number of backend services but only one processing policy for all the incoming responses from the bakend services. This processing rule has an xformbin transform as the first action so that the response payload is treated as a consumable string for the Log action that follows it. The last action in the processing rule is an xform action that sets the original HTTP status phrase from the backend.

The responses DataPower gets from the backend should be the same response the invoker receives. What happened is, since I have an xformbin action which transformed the response payload to text, the Content-Type was transformed into text/plain automatically. To solve this, I added a few lines in the last xform action in my proceasing rule which retrieves the original content type via var://context/INPUT/content-type.

My original intent was to set this variable in the first xformbin action so that I will retrieve it later in tha last xform action. But, when I tested it, I was surprised to see that I have successfully reassigned the original Content-Type already.

Here's a segment of the last xform:
<xsl:variable name="content_type" select="dp:variable('var://context/INPUT/content-type')" />
<dp:set-http-response-header name="'Content-Type'" value="$content_type" />
DISCLAIMER: I'm no expert in DataPower. In fact, it's just a recently acquired skill that is a few months old so please bare with me if my solutions seem stupid.