Monday, April 6, 2015

Error occurred while applying patches {org.wso2.carbon.server.extensions.PatchInstaller}

I have seen people complaining that WSO2 servers logs the following error message at server start up.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[2015-04-06 15:48:57,572] ERROR {org.wso2.carbon.server.extensions.PatchInstaller} -  Error occurred while applying patches
java.io.IOException: Destination '/home/jayanga/WSO2/wso2am-1.7.0/repository/components/plugins/org.eclipse.equinox.launcher.gtk.linux.x86_1.1.200.v20120522-1813' exists but is a directory
 at org.wso2.carbon.server.util.FileUtils.copyFile(FileUtils.java:145)
 at org.wso2.carbon.server.util.PatchUtils.copyNewPatches(PatchUtils.java:211)
 at org.wso2.carbon.server.extensions.PatchInstaller.perform(PatchInstaller.java:80)
 at org.wso2.carbon.server.Main.invokeExtensions(Main.java:152)
 at org.wso2.carbon.server.Main.main(Main.java:94)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.wso2.carbon.bootstrap.Bootstrap.loadClass(Bootstrap.java:63)
 at org.wso2.carbon.bootstrap.Bootstrap.main(Bootstrap.java:45)


The main reason to see such error message is, some erroneous entries in the patch metadata files for files itself.

This might happen if you try to forcefully stop the server as soon as you start the server

When the server start up, it copies the patch files. If the server is forcefully stopped at that time using [ctrl+c], patching processes get immediately stopped and patch meta-data will get corrupted.

You can get rid of this issue by removing corrupted patch related meta-data manually and restating the server, so that the server will apply all the patches form the beginning.

  1. remove [CARBON_HOME]/repository/components/patches/.metadata
  2. restart the server. (do not interrupt while starting up)

Thursday, March 26, 2015

Everyday Git (Git commands you need in your everyday work)

Git [1] is one of the most popular version control systems. In this post I am going to show you how to work with GitHub [2]. When it comes to GitHub there are thousands of public repositories. If you are interested in a project you can start working on it and contributing it. Followings are the steps and commands you will have to use while you work with GitHub.

1. Forking a repository
This is done via the GithHub [2] web site.

2. Clone a new repository
git clone https://github.com/jsdjayanga/carbon4-kernel.git

3. Get updates from the remote repository (origin/master)
git pull origin master

4. Push the updates to the the remote repository (origin/master)
git push origin master

5. Add updated files to staging
git add TestFile.java

6. Commit the local changes to the remote repository
git commit -m "Modifications to TestFile.java" --signoff

7. Set the upstream repository
git remote add upstream https://github.com/wso2/carbon4-kernel.git

8. Fetch from upstream repository
git fetch upstream

9. Fetch from all the remote repositories
git fetch --all

10. Merge new changes from upstream repository for the master branch
git checkout master
git merge upstream/master

11. Merge new changes from upstream repository for the "otherbranch" branch
git checkout otherbranch
git merge upstream/otherbranch

12. View the history of commits
git log

13. If needed to discard some commits in the local repository
First find the commit ID to which you want to revert back to. The user the following command
git reset --hard #commitId

14. To tag a particular commit
git checkout #commitid
git tag -a v1.1.1 -m 'Tagging version v1.1.1'
git push origin --tags


[1] http://git-scm.com/
[2] https://github.com/

WSO2 Carbon : Get notified just after the server start and just before server shutdown

WSO2 Carbon [1] is a 100% open source, integrated and componentized middleware platform which enables you to develop your business and enterprise solutions rapidly. WSO2 Carbon is based on OSGi framework [2]. It inherits molecularity and dynamism from the OSGi.

In this post I am going to show you how to get notified, when the server is starting up and when the server is about to shut down. 

In OSGi, bundle start up sequence is random. So you can't rely on the bundle start up sequence.

There are real world scenarios where you have some dependencies amount bundles, hence need to perform some actions before other dependent bundles get deactivated in the server shutdown.

Eg. Let's say you have to send messages to a external system. Your message sending module use your authentication module to authenticate the request and send it to the external system and your message sending module try to send all the buffered messages before the server shutdown.

Bundle unloading sequence in OSGi not happened in a guaranteed sequence. So, what would happen if your authentication bundle get deactivated before your message sending bundle get deactivated. In this case message sending module can't send the messages

To help these type of scenarios WSO2 Carbon framework provide you with a special OSGi service which can be used to detect the server start up and server shutdown

1. How to get notified the server startup

Implement the interface org.wso2.carbon.core.ServerStartupObserver [3], and register it as a service via the bundle context.

When the server is starting you will receive notifications via completingServerStartup() and completedServerStartup()


2. How to get notified the server shutdown

Implement the interface org.wso2.carbon.core.ServerShutdownHandler [4], and register it as a service via the bundle context.

When the server is about to shutdown you will receive the notification via invoke()

eg:

1
2
3
4
5
6
7
protected void activate(ComponentContext componentContext) {
 try {
     componentContext.getBundleContext().registerService(ServerStartupObserver.class.getName(), new CustomServerStartupObserver(), null) ;
 } catch (Throwable e) {
     log.error("Failed to activate the bundle ", e);
 }
}



Sunday, October 5, 2014

How to register a custom deployer in Carbon

Deployers in Axis2 are used to track the new file additions, file updates and file deletes. Writing an custom deployer is not a difficult task. A deployer is an implementation of org.apache.axis2.deployment.Deployer interface. You can find more details on how to write a deployer on : http://wso2.com/library/3708/

Once you write your custom deployer, you have to register it. Following  is how to register a custom deployer.

Add the deployer details to the component.xml file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<component xmlns="http://products.wso2.org/carbon">
   <deployers>
       <deployer>
           <directory>sample</directory>
           <extension>xml</extension>
           <class>
               org.wso2.carbon.samples.deployer.CustomDeployer
           </class>
       </deployer>
   </deployers>
</component>

As the information given in the above configuration, a directory named ‘sample’ in the location ‘repository/deployment/server’ will be monitored. Whenever file with extension ‘xml’  is added, modified or removed deployed() method will be called.

Add the following entry to the <configuration> <instructions> list of the maven-bundle-plugin in your pox.xml file.

<Axis2Deployer>CustomDeployer</Axis2Deployer>

To be responsive for real time file additions, updates and deletions configuration ‘hotupdate’ in Axis2.xml has to be set to true.

<parameter name="hotupdate" locked="false">true</parameter>

You can find a sample code in : https://github.com/jsdjayanga/How-to-register-a-custom-deployer-in-Carbon

Friday, October 3, 2014

Internal synchronization of carbon kernel (Holding transports until the kernel get ready)

Starting up sequence of internal components of the carbon kernel is crucial for the kernel to operate  properly. Most importantly kernel should not start accepting external messages until it is ready to process messages. So it is needed to delay the activation of transports until the kernel is ready. Carbon kernel is made up of OSGi based components. According to the OSGi  standards there is no definite order in which the bundles get activated.


To overcome the this sequencing problem, In carbon kernel there is special component which handles this synchronization. ‘StartupFinalizerServiceComponent’ an OSGi component, which delays the activation of transports.


If all the required services are ready by the bundle activation time, then the ‘StartupFinalizerServiceComponent’ call the ‘completeInitialization()’ method which performs the initialization of transports. But if the required services are not available at the bundle activation time, transports will not get activated. And it will wait until the required services are available.


‘StartupFinalizerServiceComponent’ is a ServiceListener. Each time a service change happens serviceChanged() method is called, and this will check for the required service list. Once all the required services are available, it calls the ‘completeInitialization()’ and activate the transports.

Tuesday, September 30, 2014

How to register a servlet from a Carbon Component

There are three ways to register a servlet in carbon.
Specifying the servlet details in the web.xml file
Specifying the servlet details in the component.xml file
Registering the servlet with httpService in your component

You can find the sample code in : https://github.com/jsdjayanga/How-to-register-a-servlet-from-a-Carbon-Component

Specifying the servlet details in the web.xml file

Specifying the servlet details in the web.xml file is not recommended when working with the carbon framework, as it has less control over the servlet when it is directly specified in the web.xml

From the remaining two, neither is bad, its totally up to the developer to decide what is best for a given scenario.

Specifying the servlet details in the component.xml file

Specifying the servlet details in the component.xml file is the easiest way of doing this.

In this approach, you need to have your HttpServlet implementation. Then you have to specify the details about your servlet in the component.xml file. Following is how you should specify details


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<component xmlns="http://products.wso2.org/carbon">
   <servlets>
       <servlet id="SampleServlet">
           <servlet-name>sampleServlet</servlet-name>
           <url-pattern>/sampleservlet</url-pattern>
           <display-name>Sample Servlet</display-name>
           <servlet-class>
               org.wso2.carbon.samples.xmlbased.SampleServlet
           </servlet-class>
       </servlet>
   </servlets>
</component>

Once you restart the servlet, with your compiled .jar in the dropins directory (repository/components/dropins), all the request to the  http://ip:port/sampleservlet will be routed to your custom servlet (org.wso2.carbon.samples.xmlbased.SampleServlet).


Registering the servlet with httpService

Registering the servlet with httpService allows dynamically register and unregister services. This allows you to have more control over the availability of the servlet.

In this approach, you need to have your HttpServlet implementation. Then you have to register your servlet with the org.osgi.service.http.HttpService once your bundle get activated.


httpService.registerServlet("/sampledynamicservlet", new SampleDynamicServlet(), null, null);

Then onwards, requests received for the http://ip:port/sampledynamicservlet will be routed to your custom servlet.

In this approach you can unregister your servlet, this cause the http://ip:port/sampledynamicservlet to be unavailable.


httpService.unregister("/sampledynamicservlet");


Custom Authenticator for WSO2 Identity Server (WSO2IS) with Custom Claims

WSO2IS is one of the best Identity Servers, which enables you to offload your identity and user entitlement management burden totally from your application. It comes with many features, supports many industry standards and most importantly it allows you to extent it according to your security requirements.

In this post I am going to show you how to write your own Authenticator, which uses some custom claim to validate users and how to invoke your custom authenticator with your web app.

Create your Custom Authenticator Bundle

WSO2IS is based OSGi, so if you want to add a new authenticator you have to crate an OSGi bungle. Following is the source of the OSGi bundle you have to prepare.

This bundle will consist of three files,
1. CustomAuthenticatorServiceComponent
2. CustomAuthenticator
3. CustomAuthenticatorConstants

CustomAuthenticatorServiceComponent is an OSGi service component it basically registers the CustomAuthenticator (service). CustomAuthenticator is an implementation of org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator, which actually provides our custom authentication.


1. CustomAuthenticatorServiceComponent


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.wso2.carbon.identity.application.authenticator.customauth.internal;

import java.util.Hashtable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator;
import org.wso2.carbon.identity.application.authenticator.customauth.CustomAuthenticator;
import org.wso2.carbon.user.core.service.RealmService;

/**
 * @scr.component name="identity.application.authenticator.customauth.component" immediate="true"
 * @scr.reference name="realm.service"
 * interface="org.wso2.carbon.user.core.service.RealmService"cardinality="1..1"
 * policy="dynamic" bind="setRealmService" unbind="unsetRealmService"
 */
public class CustomAuthenticatorServiceComponent {

    private static Log log = LogFactory.getLog(CustomAuthenticatorServiceComponent.class);

    private static RealmService realmService;
    
    protected void activate(ComponentContext ctxt) {

        CustomAuthenticator customAuth = new CustomAuthenticator();
     Hashtable<String, String> props = new Hashtable<String, String>();
     
        ctxt.getBundleContext().registerService(ApplicationAuthenticator.class.getName(), customAuth, props);
        
        if (log.isDebugEnabled()) {
            log.info("CustomAuthenticator bundle is activated");
        }
    }

    protected void deactivate(ComponentContext ctxt) {
        if (log.isDebugEnabled()) {
            log.info("CustomAuthenticator bundle is deactivated");
        }
    }
    
    protected void setRealmService(RealmService realmService) {
        log.debug("Setting the Realm Service");
        CustomAuthenticatorServiceComponent.realmService = realmService;
    }

    protected void unsetRealmService(RealmService realmService) {
        log.debug("UnSetting the Realm Service");
        CustomAuthenticatorServiceComponent.realmService = null;
    }

    public static RealmService getRealmService() {
        return realmService;
    }

}


2. CustomAuthenticator

This is where your actual authentication logic is implemented


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package org.wso2.carbon.identity.application.authenticator.customauth;

import java.io.IOException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.identity.application.authentication.framework.AbstractApplicationAuthenticator;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus;
import org.wso2.carbon.identity.application.authentication.framework.LocalApplicationAuthenticator;
import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade;
import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext;
import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException;
import org.wso2.carbon.identity.application.authentication.framework.exception.InvalidCredentialsException;
import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.application.authenticator.customauth.internal.CustomAuthenticatorServiceComponent;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

/**
 * Username Password based Authenticator
 * 
 */
public class CustomAuthenticator extends AbstractApplicationAuthenticator
        implements LocalApplicationAuthenticator {

    private static final long serialVersionUID = 192277307414921623L;

    private static Log log = LogFactory.getLog(CustomAuthenticator.class);

 @Override
 public boolean canHandle(HttpServletRequest request) {
        String userName = request.getParameter("username");
  String password = request.getParameter("password");

  if (userName != null && password != null) {
   return true;
  }

  return false;
 }

    @Override
    public AuthenticatorFlowStatus process(HttpServletRequest request,
                                           HttpServletResponse response, AuthenticationContext context)
            throws AuthenticationFailedException, LogoutFailedException {

        if (context.isLogoutRequest()) {
            return AuthenticatorFlowStatus.SUCCESS_COMPLETED;
        } else {
            return super.process(request, response, context);
        }
    }

 @Override
 protected void initiateAuthenticationRequest(HttpServletRequest request,
   HttpServletResponse response, AuthenticationContext context)
   throws AuthenticationFailedException {

  String loginPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL();
  String queryParams = FrameworkUtils
    .getQueryStringWithFrameworkContextId(context.getQueryParams(),
      context.getCallerSessionKey(),
      context.getContextIdentifier());
  
  try {
      String retryParam = "";
            
            if (context.isRetrying()) {
                retryParam = "&authFailure=true&authFailureMsg=login.fail.message";
            }
      
            response.sendRedirect(response.encodeRedirectURL(loginPage + ("?" + queryParams))
                    + "&authenticators=" + getName() + ":" + "LOCAL" + retryParam);
  } catch (IOException e) {
   throw new AuthenticationFailedException(e.getMessage(), e);
  }
 }

 @Override
 protected void processAuthenticationResponse(HttpServletRequest request,
   HttpServletResponse response, AuthenticationContext context)
   throws AuthenticationFailedException {

  String username = request.getParameter("username");
  String password = request.getParameter("password");

  boolean isAuthenticated = false;

  // Check the authentication
  try {
   int tenantId = IdentityUtil.getTenantIdOFUser(username);
            UserRealm userRealm = CustomAuthenticatorServiceComponent.getRealmService()
                    .getTenantUserRealm(tenantId);
            
            if (userRealm != null) {
                UserStoreManager userStoreManager = (UserStoreManager)userRealm.getUserStoreManager();
                isAuthenticated = userStoreManager.authenticate(MultitenantUtils.getTenantAwareUsername(username),password);

                Map<String, String> parameterMap = getAuthenticatorConfig().getParameterMap();
                String blockSPLoginClaim = null;
                if(parameterMap != null) {
                    blockSPLoginClaim = parameterMap.get("BlockSPLoginClaim");
                }
                if (blockSPLoginClaim == null) {
                    blockSPLoginClaim = "http://wso2.org/claims/blockSPLogin";
                }
                if(log.isDebugEnabled()) {
                    log.debug("BlockSPLoginClaim has been set as : " + blockSPLoginClaim);
                }

                String blockSPLogin = userStoreManager.getUserClaimValue(MultitenantUtils.getTenantAwareUsername(username),
                        blockSPLoginClaim, null);

                boolean isBlockSpLogin = Boolean.parseBoolean(blockSPLogin);
                if (isAuthenticated && isBlockSpLogin) {
                    if (log.isDebugEnabled()) {
                        log.debug("user authentication failed due to user is blocked for the SP");
                    }
                    throw new AuthenticationFailedException("SPs are blocked");
                }
            } else {
                throw new AuthenticationFailedException("Cannot find the user realm for the given tenant: " + tenantId);
            }
  } catch (IdentityException e) {
   log.error("CustomAuthentication failed while trying to get the tenant ID of the use", e);
   throw new AuthenticationFailedException(e.getMessage(), e);
  } catch (org.wso2.carbon.user.api.UserStoreException e) {
   log.error("CustomAuthentication failed while trying to authenticate", e);
   throw new AuthenticationFailedException(e.getMessage(), e);
  }

  if (!isAuthenticated) {
   if (log.isDebugEnabled()) {
    log.debug("user authentication failed due to invalid credentials.");
            }

            throw new InvalidCredentialsException();
  }

  context.setSubject(username);
  String rememberMe = request.getParameter("chkRemember");

  if (rememberMe != null && "on".equals(rememberMe)) {
   context.setRememberMe(true);
  }
 }
 
 @Override
 protected boolean retryAuthenticationEnabled() {
  return true;
 }
 
 @Override
 public String getContextIdentifier(HttpServletRequest request) {
  return request.getParameter("sessionDataKey");
 }

 @Override
 public String getFriendlyName() {
  return CustomAuthenticatorConstants.AUTHENTICATOR_FRIENDLY_NAME;
 }

 @Override
 public String getName() {
  return CustomAuthenticatorConstants.AUTHENTICATOR_NAME;
 }
}

3. CustomAuthenticatorConstants

This is a helper class to just to hold the constants you are using in your authenticaator


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package org.wso2.carbon.identity.application.authenticator.customauth;

/**
 * Constants used by the CustomAuthenticator
 *
 */
public abstract class CustomAuthenticatorConstants {
 
 public static final String AUTHENTICATOR_NAME = "CustomAuthenticator";
 public static final String AUTHENTICATOR_FRIENDLY_NAME = "custom";
 public static final String AUTHENTICATOR_STATUS = "CustomAuthenticatorStatus";
}

Once you are done with these files, your authenticator is ready. Now you can build you OSGi bundle and place the bundle inside <CRBON_HOME>/repository/components/dropins.

*sample pom.xml file [3]

Create new Claim

Now you have to create a new claim in WSO2IS. To do this, log into the management console of WSO2IS and do the steps described in [1]. In this example, I am going to create new claim "Block SP Login".

So, goto configuration section of the management console click on "Claim Management", then select "http://wso2.org/claims" Dialect

Click on "Add New Claim Mapping", and fill the details related to your claim.

Display Name   Block SP Login
Description   Block SP Login
Claim Uri http://wso2.org/claims/blockSPLogin
Mapped Attribute (s)  localityName
Regular Expression  
Display Order   0
Supported by Default  true
Required   false
Read-only   false

Now, your new claim is ready in WSO2IS. As you select "Supported by Default" as true, this claim will be available in your user profile. So you will see this field appear, when you try to create a user, but this field in not mandatory as you didn't specify it as "Required"

Change application-authentication.xml

There is another configuration change you have to do, as it is going to take the claim name from the configuration file (CustomAuthenticator.java, 107-114). Add the information about the your new claim into repository/conf/security/application-authentication.xml


1
2
3
<AuthenticatorConfig name="CustomAuthenticator" enabled="true">
<Parameter name="BlockSPLoginClaim">http://wso2.org/claims/blockSPLogin</Parameter>
</AuthenticatorConfig> 

If you check the code CustomAuthenticator.java line,107-128. You will see in the processAuthenticationResponse, in addition to authenticating the user from the user store, it checks for the new claim,

So, this finishes the, basic steps to setup your custom authentication. Now you have to setup new Service Provider in WSO2IS and set you custom authentication to it. So that when ever your SP try to authenticate a user from WSO2IS, it will use your custom authenticator.

Create Service Provider and set the Authenticator

Follow the basic steps given in [2] to create a new Service Provider.

Then, goto, "Inbound Authentication Configuration"->"SAML2 Web SSO Configuration", and make the following changes,


1
2
3
4
5
6
Issuer* = <name of you SP>
Assertion Consumer URL = <http://localhost:8080/your-app/samlsso-home.jsp>
Enable Response Signing = true
Enable Assertion Signing = true 
Enable Single Logout = true
Enable Attribute Profile = true

Then goto, "Local & Outbound Authentication Configuration" section,
select "Local Authentication" as the authentication type, and select your authenticator, here "custom".

Now you have completed all the steps needed to setup your custom autheticator with your custom claims

You can now start the WSO2IS, and start using your service. Meanwhile, change the value of the "Block SP Login" of a particular user and see the effect.


[1] https://docs.wso2.com/display/IS500/Adding+New+Claim+Mapping
[2] https://docs.wso2.com/display/IS500/Adding+a+Service+Provider
[3] https://drive.google.com/file/d/0B25Kjdxz8EhCQktfdG5MYkFnTUk/view?usp=sharing