Wednesday, December 31, 2014

Homebrew LDAP authorization with Spring Security

Continuing my journey from previous post, I realize that although LDAP authentication has been done successfully, but the user were still have no roles being assign. For this issue, I'm require to implement a custom authorization mechanism in order to assign LDAP user a role.

I found there are plenty of resources on how could an LDAP user is map to a role:
  1. configure LdapAuthenticationProvider in Spring. (read here)
  2. configure user-context-mapper-ref and extends one class with DefaultLdapAuthoritiesPopulator (read here)
  3. confgiure user-context-mapper-ref and extends one class with LdapUserDetailsMapper (read here)
  4. configure BindAuthenticator constructor and implements one class with LdapAuthoritiesPopulator (read here)
Among those resources, I found option 3 would be the most suit for my use case since I don't have the role group define in LDAP. And my requirement is as long as I got the name huahsin detected, then grant him an ROLE_ADMIN. To put nonsense short, I created a class extending LdapUserDetailsMapper:
public class MyAuthorityMapper extends LdapUserDetailsMapper {
 
 @Override
 public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection authority) {
  
  UserDetails userDetails = super.mapUserFromContext(ctx, username, authority);
  
  Collection<grantedauthority> authorities = new ArrayList<grantedauthority>();
  
  if( "huahsin".equalsIgnoreCase(username) ) {
   authorities.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
  }
  else {
   authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
  }
  
  return new User(userDetails.getUsername(), 
    userDetails.getPassword(),
    true,
    true,
    true,
    true,
    authorities);
 }
 
}
ROLE_ADMIN and ROLE_USER shown in the code snippet above is hard coded sample on how the authority is granted. In real life, the role should obtain from other source such as database. On Spring configuration site, I will have this:
    <authentication-manager alias="authenticationManager">

        <ldap-authentication-provider user-search-filter="cn={0}" group-search-base="ou=Counter Strike,ou=java,dc=homebrew,dc=org" user-context-mapper-ref="myAuthorityMapper"/>

    </authentication-manager>

    <beans:bean class="org.huahsin.WebEngineering.MyAuthorityMapper" id="myAuthorityMapper"/>

    <ldap-server url="ldap://127.0.0.1:10389"/>

Thursday, December 25, 2014

Homebrew LDAP authentication with Spring Security

The Spring Security was so interesting that I can't wait to make my own experiment at home. To do this, I'm using Apache Directory Studio to achieve this mission. For starter, I create the following structure in my LDAP:

dc=org
    |---dc=homebrew
             |---ou=java
                      |---ou=Counter Strike
                               |---cn=huahsin

Take note that when creating a CN object, SN attribute has to be define as well. Otherwise an error will be prompt. On Spring configuration site, I'm required to configure <ldap-server> element to establish connection against which authentication should take place. Following piece is more than enough for this purpose:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.1.xsd">

   ...

   <ldap-server url="ldap://127.0.0.1:10389"/>

</beans:beans>
Also take note that there is additional attributes called manager-dn and manager-password in <ldap-server>, interestingly without these attributes put in <ldap-server> element, it wouldn't fail the authentication. The process were still go on. I think there must be a purpose for these attributes, just put this aside for later. The usage of that 2 attributes are shown below:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.1.xsd">

   ...

   <ldap-server manager-dn="cn=huahsin,ou=Counter Strike,ou=java,dc=homebrew,dc=org" manager-password="abcde" url="ldap://127.0.0.1:10389"/>

</beans:beans>
Once LDAP connection has been established, authentication will start taking place immediately. Now I'll required <ldap-authentication-provider> for this purpose:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.1.xsd">

   ...

   <authentication-manager alias="authenticationManager">
      <ldap-authentication-provider user-search-filter="cn={0}"/>
   </authentication-manager>
</beans:beans>
{0} is a placeholder for CN, it will be replace by real value during runtime. For my case, it would be huahsin. Note the usage of user-search-filter, never put cn={0},ou=Counter Strike,ou=java,dc=homebrew,dc=org for this attribute, the authentication will not get pass. That should be put in user-search-group, as shown below.
   ...
   <authentication-manager alias="authenticationManager">
      < ldap-authentication-provider group-search-base="ou=Counter Strike,ou=java,dc=homebrew,dc=org" user-search-filter="cn={0}"/>
   </authentication-manager>

   ...
Another note on the usage of group-search-base attribute is when I have the LDAP define in such a way:
< beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.1.xsd" >

   ...

   <ldap-server url="ldap://127.0.0.1:10389/dc=homebrew,dc=org"/>

</beans:beans>
I should have the ou=Counter Strike,ou=java define in group-search-base as shown below:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.1.xsd">

   ...

   <authentication-manager alias="authenticationManager">
      <ldap-authentication-provider group-search-base="ou=Counter Strike,ou=java" user-search-filter="cn={0}" />
   </authentication-manager>
</beans:beans>
Otherwise following error would be seen.
 Exception thrown by application class 'org.springframework.ldap.support.LdapUtils.convertLdapException:174'

org.springframework.ldap.NameNotFoundException: [LDAP: error code 32 - NO_SUCH_OBJECT: failed for MessageType : SEARCH_REQUEST Message ID : 3 SearchRequest baseDn : 'ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org' filter : '(uniqueMember=2.5.4.3=kok hoe+2.5.4.4=loh,2.5.4.11=Counter Strike,2.5.4.11=java,0.9.2342.19200300.100.1.25=homebrew,0.9.2342.19200300.100.1.25=org)' scope : whole subtree typesOnly : false Size Limit : no limit Time Limit : no limit Deref Aliases : deref Always attributes : 'cn', 'objectClass', 'javaSerializedData', 'javaClassName', 'javaFactory', 'javaCodeBase', 'javaReferenceAddress', 'javaClassNames', 'javaRemoteLocation' org.apache.directory.api.ldap.model.message.SearchRequestImpl@303434e3 ManageDsaITImpl Control Type OID : '2.16.840.1.113730.3.4.2' Criticality : 'false' ' : ERR_648 Invalid search base ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - NO_SUCH_OBJECT: failed for MessageType : SEARCH_REQUEST Message ID : 3 SearchRequest baseDn : 'ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org' filter : '(uniqueMember=2.5.4.3=kok hoe+2.5.4.4=loh,2.5.4.11=Counter Strike,2.5.4.11=java,0.9.2342.19200300.100.1.25=homebrew,0.9.2342.19200300.100.1.25=org)' scope : whole subtree typesOnly : false Size Limit : no limit Time Limit : no limit Deref Aliases : deref Always attributes : 'cn', 'objectClass', 'javaSerializedData', 'javaClassName', 'javaFactory', 'javaCodeBase', 'javaReferenceAddress', 'javaClassNames', 'javaRemoteLocation' org.apache.directory.api.ldap.model.message.SearchRequestImpl@303434e3 ManageDsaITImpl Control Type OID : '2.16.840.1.113730.3.4.2' Criticality : 'false' ' : ERR_648 Invalid search base ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org]; remaining name 'ou=Counter Strike,ou=java,dc=homebrew,dc=org'
at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:174)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:306)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:259)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:606)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:524)
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleAttributeValues(SpringSecurityLdapTemplate.java:173)
at org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator.getGroupMembershipRoles(DefaultLdapAuthoritiesPopulator.java:215)
at org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator.getGrantedAuthorities(DefaultLdapAuthoritiesPopulator.java:185)
at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.loadUserAuthorities(LdapAuthenticationProvider.java:197)
at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:63)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:195)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:144)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:194)
at [internal classes]
Caused by: javax.naming.NameNotFoundException: [LDAP: error code 32 - NO_SUCH_OBJECT: failed for MessageType : SEARCH_REQUEST Message ID : 3 SearchRequest baseDn : 'ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org' filter : '(uniqueMember=2.5.4.3=kok hoe+2.5.4.4=loh,2.5.4.11=Counter Strike,2.5.4.11=java,0.9.2342.19200300.100.1.25=homebrew,0.9.2342.19200300.100.1.25=org)' scope : whole subtree typesOnly : false Size Limit : no limit Time Limit : no limit Deref Aliases : deref Always attributes : 'cn', 'objectClass', 'javaSerializedData', 'javaClassName', 'javaFactory', 'javaCodeBase', 'javaReferenceAddress', 'javaClassNames', 'javaRemoteLocation' org.apache.directory.api.ldap.model.message.SearchRequestImpl@303434e3 ManageDsaITImpl Control Type OID : '2.16.840.1.113730.3.4.2' Criticality : 'false' ' : ERR_648 Invalid search base ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org]
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3112)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3033)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2840)
at com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1849)
at com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1772)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:386)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:356)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:339)
at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:267)
at org.springframework.ldap.core.LdapTemplate$4.executeSearch(LdapTemplate.java:253)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:293)
... 27 more
This stack trace gave me a very good hints on what is going on behind the scene. The error has already mention Invalid search base ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org. My common logic sense told me that the value define in user-search-group has been concatenate with the one define in url. To fix this, dc=homebrew,dc=org should be remove either from <ldap-server> or <ldap-authentication-provider>.

Another complex scenario, what if there is another DC object being define in LDAP? For this experiment, I create another DC object named cybertron as shown below:

Root DSE
   |---dc=org
       |---dc=cybertron
   |---dc=org
       |---dc=homebrew
           |---ou=java
               |---ou=Counter Strike
                   |---cn=huahsin

And then I have the LDAP define in this way:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.1.xsd">

   ...

   <ldap-server url="ldap://127.0.0.1:10389/dc=cybertron,dc=org"/>

< /beans:beans >
And I'm using this piece for authentication:
   ...
   <authentication-manager alias="authenticationManager">
      <ldap-authentication-provider group-search-base="ou=Counter Strike,ou=java,dc=homebrew,dc=org" user-search-filter="cn={0}"/>
   </authentication-manager>

   ...
Don't be foo that the above code would get pass, it wouldn't because the searching criteria would become like this ou=Counter Strike,ou=java,dc=homebrew,dc=org,dc=homebrew,dc=org,dc=cybertron,dc=org. That is the reason why it fail.

Monday, December 22, 2014

Authenticate through Active Directory in Spring

There is an web application that use a very basic authentication, or I should said it was pre-code in Spring configuration just as show below:

   


 
    
       
          
            
          
      
    

Assuming the front end code is using JSF:
 
When user is trigger the login, following code will get executed.
 public String doLogin() throws IOException, ServletException {
  
  ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
  RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()).getRequestDispatcher("/j_spring_security_check");
  dispatcher.forward((ServletRequest) context.getRequest(), (ServletResponse) context.getResponse());
  FacesContext.getCurrentInstance().responseComplete();
  
  return null;
 }
The code above is a great help, it will do the authentication as what have been define in Spring configuration, and also validate on which URL does allow to access. Thanks Spring save a great effort for me as I don't need to write crazy logic on authentication. Now I want to convert this mechanism to a more elegant way, which is to integrate this application with Active Directory. This sound easy to me as there are plenty of tutorials on Internet talking about Spring security with LDAP. Wait!! It is LDAP, not Active Directory and it was clueless to me. It took me sometime reading the Spring security documentation for few times only I got to realize this -  ActiveDirectoryLdapAuthenticationProvider.
   
      
      
   
Somehow the code above is not yet perfect as it'll search through all the entry which might be a trouble to me if the entries are huge. I haven't got a clue on how could I filter base on certain group. But for now, let's get things work. Since I'm dedicating my work to let Spring handle it for me, it would be easier when comes to exception handling. The code are shown below:
   
      
         
            /faces/badCredential.xhtml
            /faces/credentialsExpired
            /faces/accountLocked
            /faces/accountDisabled
            /faces/unauthorized
         
      
   
Note that I have declare this bean as authenticationFailureHandler. In order to get that piece to work, I need to tied up this bean in authentication-failure-handler-ref as shown below:

 
 

Great! My code able to handle failure result. Let's move on. Upon successfully authenticate, authorization need to be grant. The challenge part is I'm not using the group provided in LDAP to categories access role, instead I'm making a custom one. In other words, I have my data store in database that govern the rules who have access and who don't. The best place to put this code is via authentication-success-handler-ref of form-login, and then tied this up with a Spring bean as shown below:
   
      
      
   

   
   
AuthenticateSuccessHandler is a custom made class that extends from SavedRequestAwareAuthenticationSuccessHandler. It provide me the facility to set the default page to go after a successful login. For my case, I have the user status stored in database. authorizationBo is the guy who responsible to retrieve the user status. If the status return true, I'll grant this user a ROLE_USER, otherwise no role will be granted. And I'm using the Authentication object in onAuthenticationSuccess() to determine which landing page should a user go after the authentication process. The code below reveal the logic behind the scene:
public class AuthenticateSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

 @Override
 public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws ServletException, IOException {
  
  if( authentication.getPrincipal() instanceof LdapUserDetailsImpl ) {
   
   boolean status = authorizationBo.checkAuthorization(authentication.getName());
   
   if( status ) {
    List<grantedauthority> grantedAuth = new ArrayList<>();
    grantedAuth.add(new SimpleGrantedAuthority("ROLE_USER"));
    UserDetails userDetails = new User(authentication.getName(), "", grantedAuth);
    
    SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, "", grantedAuth));
   }
  }
  
  if( authentication.getName().equals("admin") )
   setDefaultTargetUrl("/faces/administrative.xhtml");
  else
   setDefaultTargetUrl("/faces/dashboard.xhtml");
  
  super.onAuthenticationSuccess(request, response, authentication);
 }
}
Finally I have cover both the success and failure parts. My initial though is to do some crazy logic in doLogin() such as authenticating a user and then granting a user role, but I don't at the end. Spring is a great assistant in this area, just let Spring handle it all.

Tuesday, December 16, 2014

Why cmake isn't compiling?

Something has caught my attention recently, just come across the CMake and I find it interesting when having some refreshment on C++. When I'm coding, I'm very rely on IDE stuff especially on Visual Studio C++ 6.0, I used to master every single short-cut key on this tool. However I didn't aware of there is a process working behind the scene, until I knew it in one day, and I'm so curious about it.

When I know Makefile, I find that this is the “engine” working behind the scene before compiler can even start to compile. After a long learning curve on Makefile, I realize that this tool definitely not for an idiot! It is a tool that only genius will know how to use it. Until I found CMake, something seem reasonable to understand.

But be cautious, I made some stupid mistake when I first using this tool:
  1. I accidentally miss spelled the file to CMakeList. The correct file name is CMakeLists.txt. The file name end with an s and have txt extension on it.
  2. Missing path-to-source argument when issuing the command cmake. The correct format is cmake .. This is to tell cmake that the source file can be find at current level.

One nice thing about this tool is it will generate a Makefile for me which save me a lot of time. Besides Makefile it generated, it also generate other files which I don't know its usage.

Monday, December 1, 2014

NoSuchMethodError on org.hamcrest.Matcher.describeMismatch

I found an interesting error when I have the following code being used in my unit test:

Assert.assertThat(((LoggingEvent) loggingEvent.getValue()).getLevel(), CoreMatchers.is(Level.DEBUG));

Following stacktrace could be seen:
java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V 
 at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:18) 
 at org.junit.Assert.assertThat(Assert.java:865) 
 at org.junit.Assert.assertThat(Assert.java:832) 
 at org.huahsin68.EmployeeImplTest.test(EmployeeImplTest.java:62) 
 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:622) 
 at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310) 
 at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:88) 
 at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282) 
 at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:86) 
 at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120) 
 at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:33) 
 at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:45) 
 at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:118) 
 at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101) 
 at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53) 
 at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53) 
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) 
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 

An expert told me that never use the hamcrest came from JUnit 4, I'm require to download a separate hamcrest library (it is version 1.3 as of this writing), and it must come before JUnit 4 in the build path. What he told is true.

How could I unit test on the message came from log4j?

I was ask to unit test every single function that I wrote, and I was so unlucky that every function I wrote do invoke log4j. I was thinking whether I should cover those in my unit test as well? There is not much resources found on Internet, mat be due to the reason this really waste of effort or they are more concern on the logic design than this. But I'm so curious on how the code is if I'm insist to go with it. For the purpose of this, I have create an POC as shown below. Let's assume I'm going to unit test on funcA():
public class EmployeeImpl { 

 private static Logger log; 

 public static void initLog() { 
   
  log = Logger.getLogger(EmployeeImpl.class); 
  log.setLevel(Level.INFO); 
  log.setAdditivity(false); 
   
  ConsoleAppender ca = new ConsoleAppender(); 
  ca.setWriter(new OutputStreamWriter(System.out)); 
  ca.setLayout(new PatternLayout("%-5p [%t]: %m%n")); 
  log.addAppender(ca); 
 } 
  
 public static void funA() { 
  log.info("Entering into funcA"); 
 } 
}

Next I have my unit test as shown below targeting on funcA():
@RunWith(PowerMockRunner.class)
public class EmployeeImplTest { 
  
 @Mock 
 private Appender appenderMock; 
  
 @Captor 
 private ArgumentCaptor<loggingevent> loggingEvent; 

 @Test 
 public void test() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 
   
  Logger.getRootLogger().addAppender(appenderMock); 
   
  Field log = EmployeeImpl.class.getDeclaredField("log"); 
  log.setAccessible(true); 
  log.set(null, LogManager.getLogger(EmployeeImpl.class)); 
   
  EmployeeImpl.funA(); 
   
  Mockito.verify(appenderMock, Mockito.times(1)).doAppend((LoggingEvent) loggingEvent.capture()); 
  Assert.assertThat(((LoggingEvent) loggingEvent.getValue()).getLevel(), CoreMatchers.is(Level.INFO)); 
  Assert.assertThat(((LoggingEvent) loggingEvent.getAllValues().get(0)).getRenderedMessage(), CoreMatchers.equalTo("Entering into funcA")); 
 } 
}
Tada!! The test case above test 2 things; 1) Ensure the log level is INFO, 2) Ensure the message Entering into funcA. were shown. Either one is incorrect will failed the test case. In this case, the result will be pass.

Friday, September 5, 2014

No room to fit Maven-Ear-Plugin in one pom.xml

After I have the WAR file build completed, then I'm going to build the EAR file. Unfortunately it doesn’t work as in ANT. In Ant I can do all stuff of work in just one build.xml but not in Maven. Too bad huh?!! OK, I understand this can't be done but I still insist want to do it. Anyhow, there is still no EAR file being generate after the build. What a joke? No joking, Maven doesn't allow me to do that. Take the following code snippet as my use case:

    4.0.0
 
    org.huahsin
    MyWebService
    0.0.1-SNAPSHOT
    war
 
    MyWebService

    
        
         org.huahsin
         MyWebService
         0.0.1-SNAPSHOT
         war
        

        ...
        ...

    

    
        
            
             org.apache.maven.plugins
             maven-ear-plugin
             2.9.1
             
                 7
                 
                     
                         org.huahsin
                         MyWebService
                         MyWebService.war
                     
                 
                 MyWebService
             
            
 
            ...
            ...
 
        
    

The <packaging> will cause by the build failed. I mean it would working fine for building WAR file but not for building EAR file. Now I have knew the root cause, changing to ear in <packaging> line but I'm still not satisfied with the final output. Because I have a special requirement to this EAR file not to contain all other libraries except the custom build library. Now the EAR file has mess up all others libraries in it, this could a disaster when I deploy to WebSphere Application Server.

To make it clean, I create another pom.xml that only perform one task, which is EAR packaging. And this pom.xml contain only one dependency which is my newly created WAR. To make the picture clear, the WAR file should contain all only the libraries, whereas EAR file should contain only the WAR file. This is the primary objective of having a separate pom.xml, following code snippet worth thousand of these nonsense.

    4.0.0

    org.huahsin
    MyWebServiceEar
    0.0.1-SNAPSHOT
    ear

    MyWebServiceEar

    
        
         org.huahsin
         MyWebService
         0.0.1-SNAPSHOT
         war
        
    

    
        
            
            org.apache.maven.plugins
            maven-ear-plugin
            2.9.1
            
                7
                
                    
                        org.huahsin
                        MyWebService
                        MyWebService.war
                    
                
                MyWebService
            
            
        
    

Now I have another question, how could I fit 2 pom.xml in one project? I don't know??? I just put them somewhere as long as there wouldn't crash each other in the project. Am I doing this in the right approach?

Wednesday, September 3, 2014

Maven default deployment path locate at target/tomcat/webapps?

In order for me to establish a JNDI connection in Tomcat, I have the data source declare inside server.xml as shown in the code snippet below:
     
         
         
     
And then I have the JNDI data source declared in Spring like this:
    
        
        
        
        
    
Let’s do some experiment on maven-war-plugin, if this plugin went missing, an error message mention Name [comp/env] is not bound in this Context. Unable to find [comp]. as shown in the following stack trace could be seen:
javax.naming.NameNotFoundException: Name [comp/env] is not bound in this Context. Unable to find [comp].
 at org.apache.naming.NamingContext.lookup(NamingContext.java:820)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:168)
 at org.apache.catalina.deploy.NamingResources.cleanUp(NamingResources.java:988)
 at org.apache.catalina.deploy.NamingResources.stopInternal(NamingResources.java:970)
 at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
 at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5495)
 at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:160)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
If the plugin were there (as shown in following code snippet) but without any configuration being done, the above error message could be seen too.
 
  org.apache.maven.plugins
  maven-war-plugin
  2.2
 
Now if <outputDirectory> is configure to the plugin as shown in following code snippet:
 
  org.apache.maven.plugins
  maven-war-plugin
  2.2
  
   ${project.basedir}/target/tomcat/webapps
  
 
Tada! It works. Now change to use <warSourceDirectory> in the configuration as shown in following code snippet and start up the Tomcat, the same error could be seen as well.
 
  org.apache.maven.plugins
  maven-war-plugin
  2.2
  
   ${project.basedir}/src/main/webapp/
  
 
Try make some adjustment to the docBase attribute of <Context> in server.xml as shown in following code snippet, it will works again.
     
         
    ...

     
What a surprise?! If I pay close attention on the stack trace, I would notice MyService folder wasn’t there actually as mention in another message trace as shown in below:
java.lang.IllegalArgumentException: Document base C:\MyService\target\tomcat\webapps\MyService does not exist or is not a readable directory
 at org.apache.naming.resources.FileDirContext.setDocBase(FileDirContext.java:140)
 at org.apache.catalina.core.StandardContext.resourcesStart(StandardContext.java:4906)
 at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5086)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
Another message I got was whenever Maven build is trigger, Maven will expect the output to be deploy into target/tomcat/webapps folder. Since I didn’t configure <outputdirectory> in POM.xml, then Maven will go for the default path searching my web app. By adjusting the docBase attribute 2 lever up will tell Maven "Hey! go search my app there".

Tuesday, September 2, 2014

Let’s see how profiles could help in Maven build?

Taking the following code snippet as my use case:

 ${project.name}
     
 
  
   org.apache.tomcat.maven
   tomcat7-maven-plugin
   ...
   ...
  

  
   org.apache.maven.plugins
   maven-compiler-plugin
   ...
   ...
  

  
   org.apache.maven.plugins
   maven-war-plugin
   ...
   ...
  
 

In most situation, I will just run mvn clean install tomcat7:run to bring up my application instance. But somehow, in some situation, I would like to have some special configuration on the WAR packaging to be different from the regular build. For example, to exclude some particular JAR out from the build during packaging stage, if I'm using the regular configuration as shown in the code snippet above, I will hit error when I bring up my application instance due to some libraries were gone missing during packaging stage.

In order to play well on both mvn clean install tomcat7:run and mvn clean package, my colleague suggest me to use profiles for this situation. As shown in the following code snippet, another piece of maven-war-plugins configuration is created inside the <profile>:
 
    
       CUSTOM
       
         
            
               org.apache.maven.plugins
               maven-war-plugin
               2.2
               
                  %regex[WEB-INF/lib/(?!mycustomlibrary).*.jar]
               
            
         
         myfinalName
       
    
 
With this new configuration, the regular build process will continue to work as expected while I have another separate piece to build a custom made build process for packaging. But separated piece need to be done with following command:

mvn clean package –PCUSTOM

Do take note on the <finalName> usage, it allows me to specify a custom package output file name that is different from the regular ugly Maven style name.

Using regular expression during Maven packaging stage

During maven build, the default Maven implementation will package all libraries into WEB-INF/lib folder. But the requirement has remind me that I shouldn’t include those libraries in the build except those custom build library since those libraries has already been deploy into WebSphere Application Server (WAS). Thus it helps in reducing the resulting jar and making the deployment process run faster in WAS.

Now my objective is to ensure the resulting jar is clean except those custom made library. To achieve this mission, the clue is to use packagingExcludes of maven-war-plugin’s configuration, as shown in the code snippet below:

 org.apache.maven.plugins
 maven-war-plugin
 2.2
 
  ...  
 

The challenge of this mission is to exclude any other libraries except those with custom made. I have been struggling for quite some while until my colleague has discover this could be done by using regular expression as shown below:

 org.apache.maven.plugins
 maven-war-plugin
 2.2
 
  %regex[WEB-INF/lib/(?!mycustomelibrary).*.jar]  
 

The code snippet above telling Maven to exclude any libraries other than mycustomlibrary.jar during the build. This has brought up another issue to me, what if I need to retain 2 libraries while ignoring the rest during the build? Fortunately right after this discovery, I found the solution on this problem which is by using packagingIncludes where I only need to specified the particular library that need to be includes.

Tuesday, August 19, 2014

Tomcat fail to start if webservices-rt were declare as provided scope in Maven

Arrghhhh! Tomcat failed to start again! And I see the same error message again!

org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost]]

For this round was cause by the webservices-rt dependency has been declare as provided scope.
  
   org.glassfish.metro
   webservices-rt
   2.2
   provided
  
Interestingly if I declare provided scope to webservices-api, the Tomcat were just run fine.

Monday, August 11, 2014

Missing in tomcat7-maven-plugin causing Tomcat fail to start

I just felt the following stack trace very annoying and too hard to digest.
SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[/MavenTest]]
 at java.util.concurrent.FutureTask.report(FutureTask.java:122)
 at java.util.concurrent.FutureTask.get(FutureTask.java:188)
 at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
 at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:785)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[/MavenTest]]
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
 ... 6 more
Caused by: java.lang.LinkageError: loader constraint violation: loader (instance of org/apache/catalina/loader/WebappClassLoader) previously initiated loading for a different type with name "javax/servlet/ServletContext"
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
 at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
 at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
 at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
 at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1190)
 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1681)
 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
 at com.sun.xml.ws.transport.http.servlet.WSServletContainerInitializer.onStartup(WSServletContainerInitializer.java:61)
 at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5274)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 ... 6 more

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.045s
[INFO] Finished at: Mon Aug 11 17:16:16 SGT 2014
Aug 11, 2014 5:16:16 PM org.apache.catalina.core.ContainerBase startInternal
SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost]]
 at java.util.concurrent.FutureTask.report(FutureTask.java:122)
 at java.util.concurrent.FutureTask.get(FutureTask.java:188)
 at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
 at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.startup.Tomcat.start(Tomcat.java:335)
 at org.apache.tomcat.maven.plugin.tomcat7.run.AbstractRunMojo.startContainer(AbstractRunMojo.java:1018)
 at org.apache.tomcat.maven.plugin.tomcat7.run.AbstractRunMojo.execute(AbstractRunMojo.java:478)
 at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
 at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
 at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
 at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
 at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
 at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
 at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
 at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
 at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
 at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
 at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
 at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
 at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
 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.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
 at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
 at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
 at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost]]
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
 at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
 at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:785)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 ... 6 more

In short, the key error message in this stack trace was Failed to start component [StandardEngine[Tomcat].StandardHost[localhost]]. What causes fail to start? I ask to myself. I check and re-check on my pom.xml, I'm sure it has nothing to do with the dependencies, but I just wonder whether the problem could be rise from the plugin as shown in the following code snippet:
    
     ${project.name}
     
     
          ...
          ...
     

        
            
                org.apache.tomcat.maven
                tomcat7-maven-plugin
                2.0
                
                    8086
                    ${project.basedir}/target/${project.name}
                
            

            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    1.7
                    1.7
                    
                        ${endorsed.dir}
                    
                
            
        
    
Although I found the solution on similar problem on forum but it doesn’t really help me out. While struggling for a solution, I realize that missing <serverXml> tag may cause the rise of this error. The tag belongs to tomcat7-maven-plugin’s configuration telling Tomcat plugin where to locate server.xml, the master piece on how Tomcat should behave during runtime. If the tag went missing, Tomcat may gone wild. Try and error on this tag, I got the final piece on tomcat7-maven-plugin configuration. Now it should look like following code snippet:
    
        org.apache.tomcat.maven
        tomcat7-maven-plugin
        2.0
        
            8086
            ${project.basedir}/target/${project.name}
            ${project.basedir}/src/main/tomcatconf/server.xml
        
    

Tuesday, July 29, 2014

Using cocos to create a new project from scratch

cocos is a very useful command when creating a new Cocos2d-x project from scratch. However it has to be install before I can use it. But how? Due to the information on Cocos2d-x site were scatter around, I have overlook the very important piece where the manual did actually describe how it could be install. The article mention that I must first setup in order to use this tool by just run ./setup.py on console. This program can be found on Cocos2d-x root directory. I did it on Windows 8, and it works! After the setup, a new environment variable will be create on Windows. For my case, I'll see:

Variable name: COCOS_CONSOLE_ROOT
Variable value: D:\tool\cocos2d-x-3.2\tools\cocos2d-console\bin

And then I use following command to create a new project:

C:\Users\huahsin68>cocos new MyGame1 -p org.huahsin68.puzzle -l cpp -d D:\Cocos2
dxProject

Tada! A new game project is initialize for android, ios_mac, linux, win32, and wp8 platform.

Sunday, July 13, 2014

printf command could handle special character easily

Just found a better solution on handling special character in a string. In a given scenario where I have a loop to process each file from a given file list as shown below:
 while read file
 do
  newfile=$(echo "${file}" | sed 's, ,\\ ,g')
  ...
  ...

 done < ${TEMP_FILE}
Assuming ${TEMP_FILE} containing a list of files. The file is validate to see is there any space within the file name. If there is a space, prefix it with backslash. In this context, I was using sed command to handle this for me. But there is a problem where this command does not able to handle special character, such as single quote (‘) because this command only search for space and then prefix it with backslash.

I've been thinking to have all sed command for every single special characters but it seems not a wise move to me. The expert share me a though that printf command should help me out as shown in the code snippet below:

newfile=$(printf "%q" "${file}")

This solution not only fix the single quote but also space within a string. Really appreciate the help from expert.

Tuesday, July 8, 2014

Beware of -Dfile option when using mvn in Cygwin

Cygwin is my favorite tool that could allow me to execute Linux command on Windows. Sometimes I find it weird when I’m navigating to the root path of each partition where primary partition on Windows, which C:, were prefix with /cygdrive. At first I’m not so comfort with this approach but then later I started to adopt this culture in my daily routine job.

Here come to a problem when I was using Maven to install a third party dependency into my local Maven repository. When the following command was fired:

mvn install:install-file -Dfile=/cygdrive/c/Users/kok.hoe.loh/workspace/ProjectA/lib/AuditUtil.jar -DgroupId=org.huahsin.eai -DartifactId=AuditUtil -Dversion=0.0.1 -Dpackaging=jar
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building ProjectA 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.3.1:install-file (default-cli) @ ProjectA ---
[INFO] Installing C:\cygdrive\c\Users\kok.hoe.loh\workspace\ProjectA\lib\AuditUtil.jar to C:\Users\kok.hoe.loh\.m2\repository\org\huahsin\eai\AuditUtil\0.0.1\AuditUtil-0.0.1.jar
[INFO] Installing C:\Users\kok.hoe.loh\tool\Cygwin\tmp\mvninstall6980818442128433518.pom to C:\Users\kok.hoe.loh\.m2\repository\org\huahsin\eai\AuditUtil\0.0.1\AuditUtil-0.0.1.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.764s
[INFO] Finished at: Tue Jul 08 18:00:24 SGT 2014
[INFO] Final Memory: 5M/76M
[INFO] ------------------------------------------------------------------------
The build was success but the dependency was not install in my local Maven repository. There trace information has already show me the problem but I have overlook it. Notice that the dependency was install to C:\cygdrive\c\..., which is not what I want. It seems to me -Dfile option doesn't recognize Cygwin file system.

This fix was easy, just ignore the root when dealing with -Dfile, which is /cygdrive/c for my case. Following sample shows the real work.

mvn install:install-file -Dfile=/Users/kok.hoe.loh/workspace/ProjectA/lib/AuditUtil.jar -DgroupId=org.huahsin.eai -DartifactId=AuditUtil -Dversion=0.0.1 -Dpackaging=jar

Sunday, July 6, 2014

Resolving GLEW library when compile Cocos2d-x in Windows 8

This is my second try on compiling Cocos2d-x on Windows platform. Since I have successfully compile for Linux, I'm so eager to try it on Windows. Unfortunately the build were failed. This was happened in few months ago.

In my previous attempt to compile Cocos2d-x on Windows platform happened in few months ago, I was so frustrate that I'm not able to resolve the GLEW library during run-time. I know the root cause, it is either:

  1. the GLEW DLL wasn't there, or
  2. an incompatible GLEW version has been loaded.

A check on the build directory, found out there already have the GLEW DLL, so option 1 is invalid. I downloaded the GLEW library and replace the existing GLEW library and my fate still the same. Failed! Thus option 2 was out. I was running out of clue after many times of try and error, ever wonder which GLEW library did Cocos2d-x were using?

Now a few months later, when I'm looking back on the same problem, I think I'm almost there, just that I have miss out some steps left to be done. As of this writing, I'm using Cocos2d-x 3.1.1 and VS Desktop 2012 Express Edition on Windows 8. This is what I did:

  1. Right click on the project > Properties > Linker tab > General tab > Add the new GLEW library path where the library is downloaded into Additional Library Directories field.
  2. On Linker page again > Input tab > Add glew32.lib into Additional Dependencies field. (Never pick x64 version as it doesn't work.)
  3. Replace the new GLEW DLL with the one located in build path.

Remember not to make a clean build as this will wipe off all the stuff in build directory. If a clean build has accidentally been fired, just replace the GLEW DLL will do.

Thursday, July 3, 2014

Hacking on LibreOffice to bypass proxy during build

It was a relax week for me while having all my tasks commit. Since I’m quite free at the moment, I grab a copy of LibreOffice source code to play around with. When I just started to build on it, the make command return me error due to the build process unable to proceed. The reason being is wget command which require the proxy information is not provided. I know that I’m require to provide proxy information but where? Do a deep search in the source code, found out Makefile.fetch contain following code:

&& $(WGET) --progress=dot:mega -Q 0 -P "." -l 0 -nd -nH -N $1/$2 2>&1 | tee -a  $(fetch_LOGFILE) && [ $$PIPESTATUS -eq 0 ]

I think this should be the one that cause the build failed. I wasn't sure whether it works or not if I pass in the proxy information? Just give it a try:

&& $(WGET) -e use_proxy=yes -e http_proxy=xxx.xxx.xxx.xx:8080 --proxy-user=username --proxy-password=password --progress=dot:mega -Q 0 -P "." -l 0 -nd -nH -N $1/$2 2>&1 | tee -a $(fetch_LOGFILE) && [ $$PIPESTATUS -eq 0 ]

Yes! I’m rite. The build able proceed as usual.

Wednesday, July 2, 2014

ls command doesn’t work but find works!

Oh no! The ls command does not work as expected! I have the following code in my bash script that will count the total number of txt and pdf file extension.

FILECOUNT=`ls /source_file_path/${1}/*.[Tt][Xx][Tt] /source_file_path/${1}/*.[Pp][Dd][Ff] | wc -l`

Somehow this statement will not work if I schedule it in the cronjob. But it will work if I manually execute the script. Feeling so weird. I was lucky that someone from the forum giving me an advise saying that find command would be a better choice for my use case. Here comes the new code.

FILECOUNT=$(find /source_file_path/${1}/ -maxdepth 1 -name "*.[Pp][Dd][Ff]" -o -name "*.[Tt][Xx][Tt]" | wc -l)

Beware of the maxdepth option, I made a mistake that if I miss this option in the find command, it will return me all files located in the subfolder as well.

Tuesday, July 1, 2014

WinSCP namespace could not be found.

Now my file transfer program has evolve to transfer files from one server to another server. This may involve WinSCP API using C# programming language in the project. Frankly speaking, I'm a noob in C#. In order to work with WinSCP API, the first thing to do is to have WinSCPnet.dll downloaded and put it at the same level with my executable path. Next is to add a reference of this module into the project. Following steps shows how this could be done.

  1. Go to project properties > Reference Paths > Fill in the Folder field with the path where the WinSCPnet.dll is locate. 
  2. Right click on project > Add Reference > Choose Extensions under Reference Manager > Select WinSCPnet. 
Failed to do this will cause an error at following statement:

using WinSCP; 

And this error message could be seen.

The type or namespace name 'WinSCP' could not be found (are you missing a using directive or an assembly reference?)

Tuesday, June 24, 2014

Test command on file size in bash script

My objective was to capture the files locate in a particular path into a file, this is what I did in my bash script.
 # find out all available file
 files=$(ls -1 ${SRC_PATH}/*.txt)
 echo "${files}" > ${TEMP_FILE} 

 # convert the space between each file into newline
 sed 's/ /\n/g' ${TEMP_FILE} > ${PARSE_FILE}
 
 # loop through each file and check the file size is greater than zero
 for file in `cat ${PARSE_FILE}`; do

  file_size=`ls -lah ${file} | awk '{ print $5 }'`
  if [ $file_size -ne 0 ]; then
   echo $file >> "${NICE_FILE}"
  fi
 done
After a few round of review and unit testing on my code, I realize there is a problem where sed command was unable to handle file name with space within it. After a few round of rework, finally I came out a so called "perfect" solution.
 # find out all available file
 files=$(ls -1 ${SRC_PATH}/*.txt)
 echo "${files}" > ${TEMP_FILE} 

 # loop through each file and check the file size is greater than zero
 while read file 
 do
  if [ -s "${file}" ]
  then
   newfile=$(echo "${file}" | sed 's, ,\\ ,g')
    
   echo "${newfile}" >> "${NICE_FILE}"
  fi
 done < ${TEMP_FILE}
No, this is not yet the perfect solution, I'm sure there is always a better one beyond this universe.

Friday, June 20, 2014

Resolving log4j 1.2.15 dependencies in Maven

Recently I was configuring log4j 1.2.15 in Maven and I found it weird when Maven complaining this in my pom.xml.
ArtifactTransferException: Failure to transfer com.sun.jdmk:jmxtools:jar:1.2.1 from https://maven-repository.dev.java.net/nonav/repository was cached in the local repository, resolution will not be reattempted until the update interval of java.net has elapsed or updates are forced. Original error: Could not transfer artifact com.sun.jdmk:jmxtools:jar:1.2.1 from/to java.net (https://maven-repository.dev.java.net/nonav/repository): No connector available to access repository java.net (https://maven-repository.dev.java.net/nonav/repository) of type legacy using the available factories AsyncRepositoryConnectorFactory, WagonRepositoryConnectorFactory
ArtifactTransferException: Failure to transfer com.sun.jmx:jmxri:jar:1.2.1 from https://maven-repository.dev.java.net/nonav/repository was cached in the local repository, resolution will not be reattempted until the update interval of java.net has elapsed or updates are forced. Original error: Could not transfer artifact com.sun.jmx:jmxri:jar:1.2.1 from/to java.net (https://maven-repository.dev.java.net/nonav/repository): No connector available to access repository java.net (https://maven-repository.dev.java.net/nonav/repository) of type legacy using the available factories AsyncRepositoryConnectorFactory, WagonRepositoryConnectorFactory
ArtifactTransferException: Failure to transfer javax.jms:jms:jar:1.1 from https://maven-repository.dev.java.net/nonav/repository was cached in the local repository, resolution will not be reattempted until the update interval of java.net has elapsed or updates are forced. Original error: Could not transfer artifact javax.jms:jms:jar:1.1 from/to java.net (https://maven-repository.dev.java.net/nonav/repository): No connector available to access repository java.net (https://maven-repository.dev.java.net/nonav/repository) of type legacy using the available factories AsyncRepositoryConnectorFactory, WagonRepositoryConnectorFactory
Missing artifact com.sun.jdmk:jmxtools:jar:1.2.1
Missing artifact com.sun.jmx:jmxri:jar:1.2.1
Missing artifact javax.jms:jms:jar:1.1
Googling around found out that log4j is having dependencies on jmxri, jmxtools, and jms. Unfortunately there are some licensing issue from SUN that I must exclude those dependencies when retrieving log4j from Maven. This is how things get done in my pom.xml.
        
         log4j
         log4j
         1.2.15
         
          
           com.sun.jdmk
           jmxtools
       
       
               javax.jms
               jms
       
       
        com.sun.jmx
           jmxri
       
         
        

Sunday, May 25, 2014

Work around to authenticate during unit testing on Spring MVC

Following method is the "usual practice" used in unit testing the authentication on Spring MVC.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(value = {"classpath:/WEB-INF/Project-servlet.xml", "classpath:/WEB-INF/security.xml", "classpath:/WEB-INF/datasource.xml", "classpath:/WEB-INF/user.xml"})

public class HelloControllerTest extends AbstractJUnit4SpringContextTests {

 private MockMvc mockMvc;
 
 @Autowired
 private WebApplicationContext wac;
 
 @Autowired
 @Qualifier("authServiceProvider4")
 private UserDetailsService userDetailsService;

 @Autowired
 private FilterChainProxy proxy;
 
 @Before
 public void setUp() {
  mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilter(proxy).build();
 }

 @Test
 public void testUserWithAdminRoleLandOnWelcomeUrl() throws Exception {
  
  UserDetails ud = userDetailsService.loadUserByUsername("user1");
  Authentication auth = new UsernamePasswordAuthenticationToken(ud.getUsername(), ud.getPassword(), ud.getAuthorities());
  SecurityContextHolder.getContext().setAuthentication(auth);

  ...

  mockMvc.perform(get("/welcome"))
   .andExpect(status().isOk());

 }
}
Code sample above showing a test method testing on valid user, user1, landing on welcome site which require admin role in order to render the page. Somehow the test were failed, and following failure trace were seen:
java.lang.AssertionError: Status expected:<200> but was:<302>
 at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:60)
 at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:89)
 at org.springframework.test.web.servlet.result.StatusResultMatchers$5.match(StatusResultMatchers.java:546)
 at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:141)
 at org.huahsin.sit.HelloControllerTest.testUserWithAdminRoleLandOnWelcomeUrl(HelloControllerTest.java:101)
 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:622)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
 at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
 at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
 at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
 at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
 at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)


From what I understand from this answer, the session of user1 were gone missing, I need to hold the session of user1 when MockMvc doing its work. To do this, a mocking session class is require:
    public static class MockSecurityContext implements SecurityContext {

        private static final long serialVersionUID = -1386535243513362694L;

        private Authentication authentication;

        public MockSecurityContext(Authentication authentication) {
            this.authentication = authentication;
        }

        @Override
        public Authentication getAuthentication() {
            return this.authentication;
        }

        @Override
        public void setAuthentication(Authentication authentication) {
            this.authentication = authentication;
        }
    }
After that create a mock session and pass it to MockMvc to perform its test work.
 @Test
 public void testUserWithAdminRoleLandOnWelcomeUrl() throws Exception {
  
  UserDetails ud = userDetailsService.loadUserByUsername("user1");
  Authentication auth = new UsernamePasswordAuthenticationToken(ud.getUsername(), ud.getPassword(), ud.getAuthorities());
  SecurityContextHolder.getContext().setAuthentication(auth);

  MockHttpSession session = new MockHttpSession();
  session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, new MockSecurityContext(auth));

  mockMvc.perform(get("/welcome").session(session))
   .andExpect(status().isOk());

 }

Friday, May 23, 2014

Why @Secured and @PreAuthorize isn't working?

As mention in the title, I'm so curious to know why the both annotation are not working? Since there were so many examples and tutorials available on the Internet, but why none of them showing me that configuring Spring Security in such a way isn't a wise move? Which way? The finding is as follow.

As according to the Spring MVC development standard, the Spring Security configuration and servlet configuration were separated. Thus I was thinking that those security stuff together with <global-method-security> must be separated out from regular Spring configuration module and put them in Spring security configuration module.

To make the picture clearer. I have the Spring security configure in such a way:

 
 

  
   
   
  
   ...
  
   

   ...
   
  

  
  
   
   

   ...

  
 

And those non security stuff would place in regular Spring configuration module shown as below:


   

   
      
         /WEB-INF/pages/
      

      
         .jsp
      
   

And last, I have both Spring configuration declare in web.xml


   ...

   
      contextConfigLocation
      
         /WEB-INF/security.xml
         /WEB-INF/datasource.xml
         /WEB-INF/WebEngineering-servlet.xml
      
   

...


Everything were run perfect and no error. Just that @Secured will never work. Initially I though there is a bug in @Secured for Spring Security 3.1, but this is not true because the annotation were still not working even though I have upgraded to Spring Security 3.2. Someone in the forum suggest me to use @PreAuthorize simply because @PreAuthorize is newer than @Secured. I tried that, unfortunately @PreAuthorise is still working. I have tried many ways, keep searching the solutions, reading the same article again and again to make sure I didn't miss out the important point. Until my last try before I nearly give up (and before I start blaming Spring framework), I take out <global-method-security> from Spring security module, and put it in the regular Spring configuration module.

And it works!!

So this is the final amendment I made on the regular Spring configuration:


   
   

   
      
         /WEB-INF/pages/
      

      
         .jsp
      
   

And now both @Secured and @PreAuthorize are living in my code happily ever after. :)

Monday, April 7, 2014

Shell Script Programming Challenge

I'm just trying give myself a test on problem solving skill using Bash programming.

Given a scenario where the file, say file_A, locate in directory dir_A is required to rename into this format pattern <file>_yyyymmdd.txt. I’m require to write a bash script to accomplish this task. In addition to this, dir_A sometimes may contain multiple files, the script must be able to handle such situation.

Here is my work:
...
...

for XFILE in `ls ${DIR_A} | grep -v "\."`
do
   mv ${XFILE} ${XFILE}_`date +%Y%m%d`.txt
done
Surprise me! It just take 4 lines of code.

Wednesday, April 2, 2014

Failed to transfer file with SFTP in batch mode

There is a problem when I transferring files over SFTP in batch mode. Say I have the file transfer script that do this:
sshpass –e sftp –b filelist.txt theuser@123.123.12.123:/home/theuser << EOF
bye
EOF
Assuming there is no problem found in filelist.txt, if the file transfer script is execute, the following error would be seen:

Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
Couldn't read packet: Connection reset by peer


Running the sftp command with –v option shows that it seems not able to find the key pair for authentication. Later I found out that missing oBatchMode option may failed the process, thus this should be the work around I found in forum.
sshpass –e sftp –oBatchMode=no –b filelist.txt theuser@123.123.12.123:/home/theuser << EOF
bye
EOF

Tuesday, April 1, 2014

Passing authentication with SSHPASS

There is a requirement to create a cron script that will transfer files through SFTP command. But one problem I’m facing here is the script will prompt for password in order to proceed its job. This is not a correct behavior since a cron must be able to perform the task without human interaction.

Since the server is not yet ready for me to play around with, and I don’t know how to setup public/private key pair, the only way I can opt for is SSHPASS command. I think this should be the cheapest solution I could opt for. I do understand that SSHPASS is insecure and not encourage to use, but this should be fine under development stage.

Assuming I’m going to transfer a file to a destiny location at /home/theuser with username theuser and IP 123.123.12.123. With SFTP, this is what I’ll do in the script:
sftp theuser@123.123.12.123:/home/theuser << EOF
put thefile.txt
bye
EOF
Whenever this script is run, I’ll get a prompt for password then only the process will continue. Now if I do it with SSHPASS command, this is how it look like:
sshpass –p ‘password’ sftp theuser@123.123.12.123:/home/theuser << EOF
put thefile.txt
bye
EOF
There is also an alternate solution as shown below, by keeping the password in environment variable but which is strongly not a good way to go.
export SSHPASS=password
sshpass –e sftp theuser@123.123.12.123:/home/theuser << EOF
put thefile.txt
bye
EOF

Compiling C code with Informix 4GL

When I was assigned a task that involve integration work between MQ and 4GL, this was really exciting me because this will involve C programming language since C is my favorite language. But one thing to note is that this is not the regular C I’ve been doing in the pass. It is highly dependent on the version of Informix version I’m currently running.

As I ask around, the Informix environment we are running is called Rapid Development System, RDS for short. In this enviroment, there is a program that used to make the C code, named cfglgo.v2. It is not a compiler, rather it is a P-code (pseudocode) runner. My senior told me that there are also c4gl or i4gl, but these are not usable in ours environment. Following is the example on how this runner is call:

cfglgo.v2 -I/opt/mqm/inc fgiusr.c myrunner.c /opt/mqm/lib64/libmqic.so -o myrunner

Following output should be seen after the execution on the above statement:
Reading specs from /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/specs
Configured with: ../configure --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --enable-shared --enable-languages=c,c++,f77
Thread model: posix
gcc version 3.4.6
 /usr/local/libexec/gcc/sparc-sun-solaris2.10/3.4.6/cc1 -quiet -v -I/usr/informix/incl/tools -I/usr/informix/incl/esql -I/opt/mqm/inc -D__arch64__ -D__sparcv9 fgiusr.c -mptr64 -mstack-bias -mno-v8plus -mcpu=v9 -quiet -dumpbase fgiusr.c -m64 -auxbase fgiusr -version -o /var/tmp//ccNzNEmS.s
ignoring nonexistent directory "NONE/include"
ignoring nonexistent directory "/usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/../../../../sparc-sun-solaris2.10/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/informix/incl/tools
 /usr/informix/incl/esql
 /opt/mqm/inc
 /usr/local/include
 /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/include
 /usr/include
End of search list.
GNU C version 3.4.6 (sparc-sun-solaris2.10)
        compiled by GNU C version 3.3.2.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 /usr/ccs/bin/as -V -Qy -s -xarch=v9 -o /var/tmp//cc4f9CWC.o /var/tmp//ccNzNEmS.s
/usr/ccs/bin/as: Sun Compiler Common 10 Patch 09/04/2007
 /usr/local/libexec/gcc/sparc-sun-solaris2.10/3.4.6/cc1 -quiet -v -I/usr/informix/incl/tools -I/usr/informix/incl/esql -I/opt/mqm/inc -D__arch64__ -D__sparcv9 myprogram.c -mptr64 -mstack-bias -mno-v8plus -mcpu=v9 -quiet -dumpbase myprogram.c -m64 -auxbase mqfunc -version -o /var/tmp//ccNzNEmS.s
ignoring nonexistent directory "NONE/include"
ignoring nonexistent directory "/usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/../../../../sparc-sun-solaris2.10/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/informix/incl/tools
 /usr/informix/incl/esql
 /opt/mqm/inc
 /usr/local/include
 /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/include
 /usr/include
End of search list.
GNU C version 3.4.6 (sparc-sun-solaris2.10)
        compiled by GNU C version 3.3.2.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 /usr/ccs/bin/as -V -Qy -s -xarch=v9 -o /var/tmp//ccv77fcB.o /var/tmp//ccNzNEmS.s
/usr/ccs/bin/as: Sun Compiler Common 10 Patch 09/04/2007
 /usr/local/libexec/gcc/sparc-sun-solaris2.10/3.4.6/collect2 -V -Y P,/usr/lib/sparcv9 -Qy -o fglmq /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/sparcv9/crt1.o /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/sparcv9/crti.o /usr/ccs/lib/sparcv9/values-Xa.o /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/sparcv9/crtbegin.o -L/usr/informix/lib -L/usr/informix/lib/esql -L/usr/informix/lib/lib/tools -L/usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/sparcv9 -L/usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6 -L/usr/ccs/bin/sparcv9 -L/usr/ccs/bin -L/usr/ccs/lib/sparcv9 -L/usr/ccs/lib -L/usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/../../../sparcv9 -L/usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/../../.. -L/lib/sparcv9 -L/usr/lib/sparcv9 /var/tmp//cc4f9CWC.o /var/tmp//ccv77fcB.o /opt/mqm/lib64/libmqic.so /usr/informix/lib/tools/libfmain.a /usr/informix/lib/tools/libfglgo.a /usr/informix/lib/tools/lib4gl.a /usr/informix/lib/tools/lib4io.a /usr/informix/lib/tools/libnmenu.a /usr/informix/lib/tools/lib4io.a /usr/informix/lib/tools/librdsterm.a /usr/informix/lib/tools/libfmain.a /usr/informix/lib/tools/libfe.a /usr/informix/lib/tools/libfmain.a -ltermlib -liffgisql -lifasf -lifgen -lifos -lifgls -lnsl -lsocket -ldl -lm /usr/informix/lib/esql/checkapi.o -lifglx -laio -lm -ldl -lelf -ltermlib -lgcc -lgcc_eh -lc -lgcc -lgcc_eh -lc /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/sparcv9/crtend.o /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/sparcv9/crtn.o
ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.1518

The final output of this will generate a custom runner called myrunner. This runner will contain the required C implementation that is needed to execute a 4GL program. On 4GL site, the program must compile using fglpc in order to link with the runner. Assuming the the_4gl_program.4gl has successfully compile into the_4gl_program.4go, this would be the step to launch 4GL program:

myrunner the_4gl_program