Friday, July 31, 2015

SVN copy task causing duplicate path?

There is a problem with the <copy> task after deployed to build server. The path will become duplicate when it is executing causing the path, unable to be retrieve:
   ...

   [svn] svn: File not found: revision 1768, path '/trunk/MessageFlow/trunk/MessageFlow'

   ...
Notice that the path has been duplicated. Here is the existing ANT source:
   ...
   <typedef resource="org/tigris/subversion/svnant/svnantlib.xml" classpathref="classpath"/>

   ...

   
      <copy srcUrl="${svnTrunkRoot}/MessageFlow}" destUrl="${svnTagsRoot}/MessageFlow/${newtagname}" message="Tagged by Jenkins."/>
   
As from my study, this is a known error for SVN version 1.7 and above. Luckily opticyclic has developed a new ANT clone which can get rid of this problem. Grab the piece and build it from source. The output will have this, svntask-1.1.1.jar. When deploy to build server, sequence-library-1.0.2.jar, sqljet-1.1.10.jar, svnkit-1.8.5.jar are also needed. There are gang of four, no one left behind.

Without messing up with the existing svn task, I create another task named svn2.
   <path id="svn2.classpath">
      <pathelement location="svntask-1.1.1.jar">
      <fileset dir= "../lib">
         <include name= "*.jar"/>
      </fileset>
   </path>

   <taskdef name="svn2" classname="com.googlecode.svntask.SvnTask" classpathref="svn2.classpath"/>
To use the new copy task command, do this:
   ...
   <svn2 username="admin" password="admin">
      <copy failOnDstExists="true" move="false"
            src="${svnTrunkRoot}/MessageFlow"
            dst="${svnTagsRoot}/MessageFlow/${newtagname}"
            commitMessage="Tagged by Jenkins."/>
   </svn2>
Do take note that the new svn2 doesn't support refid attribute, thus username and password are require whenever svn2 command is invoke.

Last note, this is last minute finding, antlr-runtime-3.4.jar may also needed as I deployed to build server. Otherwise an run-time error will be thrown.

Checking SVN path existence using ANT

The use case here was to check whether the path has already existed in SVN using ANT. I was staring on the script (shown below) for a very long time and wondering how could I improve the piece to make it more interesting and sexy.
   <if>
      <svnExists target="${svnPath}/MessageFlow/${tagname}" refid="svn.settings"/>
      <then>
         ...
      </then>
   </if>

   <if>
      <not><svnExists target="${svnPath}/MessageFlow/${tagname}" refid="svn.settings"/></not>
      <then>
         ...
      </then>
   </if>
After some rearrangement then I come out this piece:
   <condition property="exists">
      <svnExists target="${ svnPath}/MessageFlow/${tagname}" refid="svn.settings"/>
   </condition>

   <!—if exists is false -->
   <if>
      <not><equals arg1="${exists}" arg2="true"/></not>
      <then>
         ...
      </then>
   </if>

   <!—if exists is true -->
   <if>
      <equals arg1="${exists}" arg2="true"/>
      <then>
         ...
      </then>
   </if>
The difference on the newer piece is that I've adopted <condition> in the script and the result was keep in exists property. Would it be better? Errrrr... It seems that a lot more code need to be done to achieve the same result.

Monday, July 27, 2015

Continuous build on Message Flow with Jenkin 3

Since the build was so successful on last time. My colleague, Ming Kuan, doesn't want to let Jenkins stops. He is pushing Jenkins to next level – deploy to Message Broker after the build. Not much information could be found on the Internet for this issue. But he was successfully implemented it on Jenkins. He is a ranger, warrior, a real fighter among the team member. What he did was he created a BATCH script for the deployment job and then this BATCH script was triggered from ANT script.

This is the ingredient for ANT, its job only to trigger BATCH:
< target name="deploy">
   <exec executable="cmd">
      <arg value="/c"/>
      <arg value="${deploy_file_path}/deploy.bat"/>
      <arg value="${bar_file}.bar"/>
   </exec>
</target>

This is the ingredient for BATCH:
call "C:/Program Files/IBM/MQSI/8.0.0.0/bin/mqsiprofile.cmd"

SET EXE_PATH="C:/Program Files/IBM/MQSI/8.0.0.0/classes/*;C:/Program Files (x86)/IBM/WebSphere MQ/java/lib/*"

SET PATH=%PATH%;C:/Program Files/IBM/MQSI/8.0.0.0/jre16/bin;C:/Program Files/IBM/MQSI/8.0.0.0;

SET BAR_FILE =%1

java -cp %EXE_PATH% com.ibm.broker.config.util.Deploy -i 192.168.1.8 -p 1818 -q Q1 -e E1 -a %BAR_FILE% -w 600

java -cp %EXE_PATH% com.ibm.broker.config.util.MessageFlowControl -stop -i 192.168.1.8 -p 1818 -q Q1 -e E1

java -cp %EXE_PATH% com.ibm.broker.config.util.MessageFlowControl -start -i 192.168.1.8 -p 1818 -q Q1 -e E1

*Alert! Notice there is a path, C:/Program Files/IBM/MQSI/, being pre-code in the BATCH. This path contains various raw materials to feed the deployment process. Thus, make sure WebSphere MQ was installed on the system. If it doesn’t, C:/Program Files/IBM/MQSI/ will never exist on the system.

The com.ibm.broker.config.util.Deploy is just like the regular mqsideploy command use in ours daily job for deployment purpose. And com.ibm.broker.config.util.MessageFlowControl is just like mqsistartmsgflow and mqsistopmsgflow use to restart the execution group. The reason for this is to make sure the deployment will take effect immediately after deployment.

Nice work from our warrior, Ming Kuan.

Keep on building, Jenkins!

Thursday, July 23, 2015

What the hell? Wrong instance of javax/management/MBeanServer?

Arghhhhh! This error happened again! I’m feeling weird on this error as I never invoke any MBeanServer thing in my code. And this error sound familiar to me, I think I have seen the similar error happened before, but I couldn’t recall my memory. I wasn't sure whether this is a common defect only happened on Powermock 1.6 but it is quite annoying me.
Caused by: java.lang.LinkageError: loader constraint violation: loader (instance of org/powermock/core/classloader/MockClassLoader) previously initiated loading for a different type with name "javax/management/MBeanServer"
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
 at org.powermock.core.classloader.MockClassLoader.loadUnmockedClass(MockClassLoader.java:237)
 at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:182)
 at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:68)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
 at org.apache.logging.log4j.core.jmx.Server.unregisterAllMatching(Server.java:307)
 at org.apache.logging.log4j.core.jmx.Server.unregisterLoggerContext(Server.java:229)
 at org.apache.logging.log4j.core.jmx.Server.reregisterMBeansAfterReconfigure(Server.java:140)
 at org.apache.logging.log4j.core.jmx.Server.reregisterMBeansAfterReconfigure(Server.java:119)
 at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:356)
 at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:386)
 at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:149)
 at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:84)
 at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:35)
 at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:444)
 at com.awpl.worker.AbstractWorker.<init>(AbstractWorker.java:60)
 at com.awpl.worker.WorkerWithTriggerFile.<init>(WorkerWithTriggerFile.java:22)
 at com.awpl.worker.WorkerWithTriggerFileTest.throwFileNotFoundException(WorkerWithTriggerFileTest.java:23)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
 ... 23 more 
So what I did is just ignore this particular error and my unit test is proceed as usual:
 @PowerMockIgnore({"javax.management.*"})
 public class TheClassTest {
    …
 }

Monday, July 20, 2015

Cannot use javahl nor command line svn client

I do not know since when the 3 idiots svnant.jar, svnClientAdapter.jar, svnjavahl.jar was placed inside ANT_ROOT/lib? I'm feeling so unhappy with this as it has caused me a nasty shit being thrown when <svn> tag was invoked!
    Trying to override old definition of task svn
    
    BUILD FAILED
    ...
    C:\TestWorkspace\base.xml:78: Cannot use javahl nor command line svn client
    
    Total time: 0 seconds
When I was about to throw them away, I notice the last modified date was at 2006 which is version 1.0.0 as according to the source repo. Surprise me! But I thought I had overridden the classpath in ANT script, why it isn’t working?
    <path id="classpath">
     <pathelement location="svnant.jar"/>
     <pathelement location="svnClientAdapter.jar"/>
     <pathelement location="svnjavahl.jar"/>
     <pathelement location="svnkit.jar"/>
     <pathelement location="ant-contrib-0.3.jar"/>
     <fileset dir= ".." >
      <include name= "*.jar" />
     </fileset>
    </path>
I rebuild again to see if the error still persists after I throw the 3 idiots far far far away. The error is gone but another one comes out:
    co:
          [svn] Missing 'javahl' dependencies on the classpath !
    
    BUILD FAILED
    ...
    C:\TestWorkspace\base.xml:80: Cannot find javahl, svnkit nor command line svn client
Surprise me again! Why this happens? Spent some time reading through the forums and articles then only I realize there are missing thing in the ANT script:
    <svnSetting svnkit="true" javahl="false" username="admin" password="admin" id="svn.settings" />
    <svn refid="svn.settings">
     ...
    </svn>
Frankly speaking, I heavily rely on the IDE. The <svn> tag wasn’t shown in editor, and I though this is a fault. Anyhow, one quick note, as of this writing, I’m using version 1.3.1. Thus svntask.properties is no longer working:

    <typedef classpathref="classpath" resource="svntask.properties"/>

That one was used in version 1.0.0. The correct way is this:

    <typedef resource="org/tigris/subversion/svnant/svnantlib.xml" classpathref="classpath"/>

Sunday, July 19, 2015

Infinite loop in Filter after sendRedirect()

My intention is to redirect to another site whenever the User-Agent has detected the mobile keyword. I have a solution for this approach where I have the file name for mobile version start with m and reside at the location same as desktop version. I try not to branch out another path for mobile version to keep maintenance easy.

Unfortunately the Filter call was unintentionally looping on itself. This seems to me that for every new redirection Filter call, the mobile keyword validation will get invoked and the new redirection is sent again. And this process will go on endlessly. This is so not good, I was trapped!

This is what happened on my code:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
   
   httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()));
  }
  else {  
   chain.doFilter(request, response);
  }
According to the advice from expert. I'm going to need additional validation to stop the process after I reach to that URI. Thus, what I need to do is to put additional validation as below:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {

   // stop redirect if the site has reach
   if( httpServletRequest.getRequestURI().equalsIgnoreCase(httpServletRequest.getContextPath() + "/" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()))) {
    chain.doFilter(request, response);
   }
   else {
    httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()));
   }
  }
  else {  
   chain.doFilter(request, response);
  }
Tested and worked as expected. I did also tried to forward the request as shown below:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
   
   request.getRequestDispatcher("m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length())).forward(request, response);
  }
  else {  
   chain.doFilter(request, response);
  }
I find this working as well but for subsequent call on doFilter, the httpServletRequest.getRequestURI() will always stuck on the old URI. I think this is due to the reason URL is forward on server site, but not reflected on client site.

Saturday, July 18, 2015

Service service jboss.ejb.default-resource-adapter-name-service not found

The default JBoss configuration in standalone.xml doesn't support messaging service. Following changes are needed in order to enable messaging service.

1. Add messaging extension.
    <extensions>
        ...
        <extension module="org.jboss.as.messaging"/>
    </extensions>

2. Add messaging subsystem.
        <subsystem xmlns="urn:jboss:domain:messaging:1.1">
            <hornetq-server>
                <persistence-enabled>true</persistence-enabled>
                <journal-file-size>102400</journal-file-size>
                <journal-min-files>2</journal-min-files>

                <connectors>
                    <netty-connector name="netty" socket-binding="messaging"/>
                    <netty-connector name="netty-throughput" socket-binding="messaging-throughput">
                        <param key="batch-delay" value="50"/>
                    </netty-connector>
                    <in-vm-connector name="in-vm" server-id="0"/>
                </connectors>

                <acceptors>
                    <netty-acceptor name="netty" socket-binding="messaging"/>
                    <netty-acceptor name="netty-throughput" socket-binding="messaging-throughput">
                        <param key="batch-delay" value="50"/>
                        <param key="direct-deliver" value="false"/>
                    </netty-acceptor>
                    <in-vm-acceptor name="in-vm" server-id="0"/>
                </acceptors>

                <security-settings>
                    <security-setting match="#">
                        <permission type="send" roles="guest"/>
                        <permission type="consume" roles="guest"/>
                        <permission type="createNonDurableQueue" roles="guest"/>
                        <permission type="deleteNonDurableQueue" roles="guest"/>
                    </security-setting>
                </security-settings>

                <address-settings>
                    <address-setting match="#">
                        <dead-letter-address>jms.queue.DLQ</dead-letter-address>
                        <expiry-address>jms.queue.ExpiryQueue</expiry-address>
                        <redelivery-delay>0</redelivery-delay>
                        <max-size-bytes>10485760</max-size-bytes>
                        <address-full-policy>BLOCK</address-full-policy>
                        <message-counter-history-day-limit>10</message-counter-history-day-limit>
                    </address-setting>
                </address-settings>

                <jms-connection-factories>
                    <connection-factory name="InVmConnectionFactory">
                        <connectors>
                            <connector-ref connector-name="in-vm"/>
                        </connectors>
                        <entries>
                            <entry name="java:/ConnectionFactory"/>
                        </entries>
                    </connection-factory>
                    <connection-factory name="RemoteConnectionFactory">
                        <connectors>
                            <connector-ref connector-name="netty"/>
                        </connectors>
                        <entries>
                            <entry name="RemoteConnectionFactory"/>
                            <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/>
                        </entries>
                    </connection-factory>
                    <pooled-connection-factory name="hornetq-ra">
                        <transaction mode="xa"/>
                        <connectors>
                            <connector-ref connector-name="in-vm"/>
                        </connectors>
                        <entries>
                            <entry name="java:/JmsXA"/>
                        </entries>
                    </pooled-connection-factory>
                </jms-connection-factories>

                <jms-destinations>
                    <jms-queue name="testQueue">
                        <entry name="queue/test"/>
                        <entry name="java:jboss/exported/jms/queue/test"/>
                    </jms-queue>
                    <jms-topic name="testTopic">
                        <entry name="topic/test"/>
                        <entry name="java:jboss/exported/jms/topic/test"/>
                    </jms-topic>
                </jms-destinations>
            </hornetq-server>
        </subsystem>

3. Bind the socket.
    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
        ...

        <socket-binding name="messaging" port="5445"/>
        <socket-binding name="messaging-throughput" port="5455"/>
    </socket-binding-group>

4. Get ready the MDB.
@MessageDriven(name="MessageMDBSample", activationConfig ={
  @ActivationConfigProperty(propertyName="destination", propertyValue="queue/test"),
  @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
  @ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue="Auto-acknowledge")
})
public class MDBSample implements MessageListener {

 @Override
 public void onMessage(Message msg) {
  ...
  ...
 }
}
OK, I'm done with all the setup. But when I startup the server, I hit following error. Why?
19:36:34,742 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-5) MSC000001: Failed to start service jboss.deployment.unit."web2.war".PARSE: org.jboss.msc.service.StartException in service jboss.deployment.unit."web2.war".PARSE: JBAS018733: Failed to process phase PARSE of deployment "web2.war"
 at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:166) [jboss-as-server-7.4.0.Final-redhat-19.jar:7.4.0.Final-redhat-19]
 at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1980) [jboss-msc-1.1.5.Final-redhat-1.jar:1.1.5.Final-redhat-1]
 at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1913) [jboss-msc-1.1.5.Final-redhat-1.jar:1.1.5.Final-redhat-1]
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) [rt.jar:1.7.0]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) [rt.jar:1.7.0]
 at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0]
Caused by: org.jboss.msc.service.ServiceNotFoundException: Service service jboss.ejb.default-resource-adapter-name-service not found
 at org.jboss.msc.service.ServiceContainerImpl.getRequiredService(ServiceContainerImpl.java:625) [jboss-msc-1.1.5.Final-redhat-1.jar:1.1.5.Final-redhat-1]
 at org.jboss.as.ejb3.deployment.processors.MessageDrivenComponentDescriptionFactory.getDefaultResourceAdapterName(MessageDrivenComponentDescriptionFactory.java:278)
 at org.jboss.as.ejb3.deployment.processors.MessageDrivenComponentDescriptionFactory.processMessageBeans(MessageDrivenComponentDescriptionFactory.java:155)
 at org.jboss.as.ejb3.deployment.processors.MessageDrivenComponentDescriptionFactory.processAnnotations(MessageDrivenComponentDescriptionFactory.java:82)
 at org.jboss.as.ejb3.deployment.processors.AnnotatedEJBComponentDescriptionDeploymentUnitProcessor.processAnnotations(AnnotatedEJBComponentDescriptionDeploymentUnitProcessor.java:58)
 at org.jboss.as.ejb3.deployment.processors.AbstractDeploymentUnitProcessor.deploy(AbstractDeploymentUnitProcessor.java:81)
 at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:159) [jboss-as-server-7.4.0.Final-redhat-19.jar:7.4.0.Final-redhat-19]
 ... 5 more
But later I found out that I was missing this:
        <subsystem xmlns="urn:jboss:domain:ejb3:1.4">
            <mdb>  
                <resource-adapter-ref resource-adapter-name="hornetq-ra"/>  
                <bean-instance-pool-ref pool-name="mdb-strict-max-pool"/>  
            </mdb>
        </subsystem>
Apologize on my silly mistake.

Tuesday, July 14, 2015

Continuous build on Message Flow with Jenkin 2

Not long ago, I’d done it once, but failed. That was happened on past 2 months ago. But now he is back again. Thanks to my colleague on his effort to discover this feature. This round ANT is used in the mission. First we try on this approach:
    < target name="create_bar">
     <mkdir dir="${dist.path}"/>
     <exec dir="." executable="C:/Program Files (x86)/IBM/WMBT800/mqsicreatebar.exe" failonerror="false" >
      <redirector output="error.log" alwayslog="true"/>     
      <arg line="-data . "/>
      <arg line="-b ProjectA/BARs/ProjectA.bar"/> 
      <arg line="-cleanBuild"/>
      <arg line="-l ProjectA"/> 
     </exec> 
    </target>
Pay attention on the argument –data and –b, the value might be different depends on the file structure. For my case, I put the build.xml inside the project folder. There is a con on this approach where it takes approximately 3 minutes to complete a build. Why does it take so long? The text below was extract from this document for easy reference.

3.2 Execution time It will be noted that when the BAR file is built from the command line, the time taken is often noticeably longer than time taken when built from the WMB ToolKit. This is because the mqsicreatebar command actually runs the Eclipse client itself in a 'headless' mode. The Eclipse front end probably keeps track of various file names, project references and lots of other parameters that speeds up the build time. As this does not happen from command line, the time taken is noticeably longer.
The second approach we did was this:
    < target name="package_bar">
     <mkdir dir="${dist.path}"/>
     <java classname="com.ibm.broker.config.appdev.FlowRendererBAR" failonerror="true" fork="true">
      <arg line="-a ${dist.path}/ProjectA.bar"/>
      <arg line="-w ../"/>
      <arg line="-y commonJava commonESQL ProjectA"/>
      <classpath>
       <fileset dir="C:/Program Files/IBM/MQSI/8.0.0.3/classes">
        <include name="*.jar"/>
       </fileset>
      </classpath>
     </java>
    </target>
With this approach, the execution time is damn fast. I’m feeling much better with this approach.

Reference resource

Friday, July 10, 2015

Detecting web's mobile version with Filter

What a boring day, while I was dreaming on my desk, something has caught my attention. I realize that in most of the time, my web application was run on desktop platform, and never know what would happen if I run the it on mobile platform? I was thinking this could be a disaster for user. But how? Could I do the validation checking in controller bean? Just like this:
If mobile device then render mobile site otherwise render desktop site.
But in reality, what I got from the Internet majority are done using JavaScript, some use CSS to accomplished the task. But this doesn't help because mine technology used was JSF and J2EE stuff. Thus I would use Filter for my case. See my action on following content:
package org.huahsin;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter(filterName="MyFilter", urlPatterns={"/pages/*", "*.xhtml"})
public class MyFilter implements Filter {

 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
  // TODO Auto-generated method stub

 }

 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // TODO Auto-generated method stub
  
  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  
  // redirect to mobile version
  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
    ...
    ...
  }
  else {  
   chain.doFilter(request, response);  // forward to original request
  }
 }

 @Override
 public void destroy() {
  // TODO Auto-generated method stub

 }

}
This code will easily know where does the request come from. I'm so curious to know what is actually hiding in User-Agent? Did a quick diagnose on it and found the following string will be retrieve when the site was render on desktop browser:
Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
And this is the string I got if I render the site on iPad:
Mozilla/5.0 (iPad; CPU OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F69 Safari/600.1.4

Wednesday, July 8, 2015

How to open access to public in JBoss?

Ever wonder why I can only access my web application from localhost in JBoss? Why I could not access it from other devices, such as mobile phone, tablet, and even other PC? I think this is due to the default configuration when JBoss was first setup:
    < interfaces>
        <interface name="management">
            <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
        </interface>
        <interface name="public">
            <inet-address value="${jboss.bind.address:127.0.0.1}"/>
        </interface>
        <interface name="unsecure">
            <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
        </interface>
    </interfaces>
A minor change is require to open access from public. What I did was to replace the <inet-address ... /> with <any-ipv4-address> in <interface name="public">.
    <interfaces>
        <interface name="management">
            &lt inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
        </interface>
        <interface name="public">
            <any-ipv4-address/>
        </interface>
        <interface name="unsecure">
            <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
        </interface>
    </interfaces>
Hey! What is unsecure by the way? Feeling uncomfortable with the word.

Saturday, July 4, 2015

Reading Message Broker property value has cause memory leak

This is my first time encountering memory leak happened in Java code. I’m feeling so ridiculous about this because I always believe that Java is a language that I should not worry about memory leak. There is one web service I was working on have spotted an abnormal behaviour usage and eventually will force the Execution Group stop functioning. This abnormal behaviour is hard to spot on my own until I run a load test on it (the load test was execute with SOAP UI).

But later have found out the root cause was happened when reading the properties values from configurable service defined in Message Broker. As according to the documentation, whenever reading the properties values with BrokerProxy as shown in the following code, it is advisable to invoke brokerProxy.disconnect() to free up resources, otherwise the memory will keep piling up causing memory leaks.
    public class JavaNode extends MbJavaComputeNode {
    
     @Override
     public void evaluate(MbMessageAssembly inAssembly) throws MbException
     {
      BrokerProxy brokerProxy;
      try {
       brokerProxy = BrokerProxy.getLocalInstance();
       brokerProxy.getConfigurableService("USERDEFINED", "key");
      } catch (ConfigManagerProxyLoggedException e1) {
       // TODO Auto-generated catch block
       e1.printStackTrace();
      } catch (ConfigManagerProxyPropertyNotInitializedException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
    
      ...
     }
    }

Thus what I did is to invoke the disconnect() in finally clause. But have to be aware that after the resources had been freed, brokerProxy will no longer usable and have to be renew. Below are some useful resources I have read in solving the issue:
  1. Important facts about garbage collection in WebSphere Message Broker
  2. Class BrokerProxy
  3. Connecting to a broker from a CMP application