Wednesday, March 13, 2013

How to manage multi site using dispatcher in CQ / WEM

Use Case:

1) You have multi language site and you want to have different dispatcher configuration for them
2) Activating pages under english site should not flush pages under french site for example
3) I have different URL for each site (for example en.mysite.com and fr.mysite.com)

Solution:

At Publish Instance

First of all, In order to manage multi domain multi language site, You should have proper mapping rule within CQ. Please see http://dev.day.com/content/kb/home/cq5/CQ5SystemAdministration/HowToMapDomains.html for that

Now what this is going to do is, make sure that your links are properly written when you open any page under different domain.

Example of sample /etc/map

map
  +-- http
    +-- any_geometrixx.de (nodetyp: sling:Mapping)
        property: internalRedirect : /content/geometrixx/de.html
        property: sling:match : .*.*geometrixx.de.(4503|80)+/$
    +-- libs (nodetyp: sling:Mapping)
          property: internalRedirect: /libs
      +-- etc (nodetyp: sling:Mapping)
        +-- designs (nodetyp: sling:Mapping)
            property: internalRedirect: /etc/designs
    +-- any_geometrixx.en (nodetyp: sling:Mapping)
        property: internalRedireect: /content/geometrixx/en.html
        property: sling:match : .*.*geometrixx.en.(4503|80)+/$
      +-- libs (nodetyp: sling:Mapping)
          property: internalRedirect: /libs
      +-- etc (nodetyp: sling:Mapping)
        +-- designs (nodetyp: sling:Mapping)
            property: internalRedirect: /etc/designs


At Dispatcher

First of all you have to associate dispatcher handler with each incoming domain. You can use Name Virtual Host for this

better to create different farm for different domain

# Each farm configures a set of load balanced renders (i.e. remote servers)
/farms
  {
       $include farm*.any
  }

Suppose you have two farms,

farm_site1.any
farm_site2.any

Then you will have virtual host setting as,

 /virtualhosts
      {
 
      "*site1*"
      }

 /renders
      {
       $include "renders.any"
      }

/cache
   /docroot "<Global Doc root>/site1"

And

/virtualhosts
      {
 
      "*site2*"
      }


    /renders
      {
       $include "renders.any"
      }

  /cache
    /docroot "<Global Doc root>/site2"

You can have same or different renderers for different site.

Now at your virtual host setting you will configure different doc root for different site

NameVirtualHost *:80

<VirtualHost *:80>
    ServerName site1.com
    #This is to handle dev enviornments
    ServerAlias *.site1.com
    DocumentRoot <Global Doc root>/site1
    Include <Configurations specific to site1>
 
</VirtualHost>

<VirtualHost *:80>
    ServerName site2.com
    #This is to handle dev enviornments
    ServerAlias *.site2.com
    DocumentRoot <Global Doc root>/site2
    Include <Configurations specific to site2>
 
</VirtualHost>
DocumentRoot <Global Doc root>

Now to handle dispatcher flush request for specific site path, You can have rule like,


SetEnvIfNoCase CQ-Path ^/content/site1 hostnameforfarm=site1.com
SetEnvIfNoCase CQ-Path ^/content/site2 hostnameforfarm=site2.com
RequestHeader set Host %{hostnameforfarm}e env= hostnameforfarm

Now above rule will set the host name for dispatcher based on {CQ-Path} in flush request. That mean if some thing under site1 get activated site2 cache will NOT be flushed.

I know there could be many variation to this, I just gave basic example. Let me know if you have more questions.

How to cache Error page in CQ

Use Case:

1) You want to serve your error page from dispatcher
2) Error pages are creating a lot of load on publish instance

Prerequisite:

  • http://sling.apache.org/site/errorhandling.html
  • http://dev.day.com/docs/en/cq/current/developing/customizing_error_handler_pages.html
  • http://dev.day.com/docs/en/cq/current/deploying/dispatcher/disp_config.html
Assumption: You are using apache as webserver

Solution:

1) Create your custom error handler using pre-requisite doc
2) Set status code in your error handler
for example:
for 404 in /apps/sling/servlet/errorhandler/404.jsp use response.setStatus(404) do not do any redirect. In fact you can have just one line (In publish).
3) Then in your dispatcher set DispatcherPassError to 1

<IfModule disp_apache2.c>
       # All your existing configuration  
       DispatcherPassError 1
</IfModule>

4) Then configure your error document in httpd.conf (Or your custom configuration file), Something like

<LocationMatch \.html$>
    ErrorDocument 404 /404.html
    #ErrorDocument <ANY OTHER ERROR CODE>  <PATH IN YOUR DOCROOT>
</LocationMatch>

Above rule mean, For any .html request if you get 404 response show /404.html page. All other extension will be handled by dispatcher 404 response (As shown below). This is a good approach in case you don't want to load your 404 (With all CSS and JS) for all other extensions.

You can have customize error file as well, Something like

<LocationMatch "/india">
    ErrorDocument 404 /404india.html
    #ErrorDocument <ANY OTHER ERROR CODE>  <PATH IN YOUR DOCROOT>
</LocationMatch>









With above configuration if someone request a page, that may lead to any of above error code, in that case page will be served by CQ dispatcher and not by Publish instance. This will also avoid unnecessary load on publish instance.

Special Thanks to Andrew Khoury from Adobe for sharing this.

Sunday, March 3, 2013

How to Create Custom Authentication Handler in CQ

Use Case:
  • You want to use custom Authentication handler instead of OOTB one for authentication. 
  • Custom User Registration
Pre requisite:
Available Authentication Handler in CQ:



How to create your Own:


1) Create custom class extending Sling Authentication Handler and override available methods


2) Create a form (Your custom Login form) 

which will be something like this

String action = currentPage.getPath() +"/j_mycustom_security_check";

<form method="POST" action="<%= xssAPI.getValidHref(action) %>"> 
Enter User Name: <input  name="j_username" type="text" />
Enter Passord: <input  name="j_password" type="text" />
<input type="button" name="Click Here to login">
</form>

You can also use Ajax post or something to see if response is 200 (Which mean successful login)

3) Then under apache sling post servlet, Make sure that you allow parameter you are posting. In this case j_*


4) Add your custom authentication prefix to sling authenticator service


5) Once you have your bundle deployed, You should see your additional authentication handler. 




Integrate it with Custom Pluggable Login Module (AEM 6)

Step1 : create pluggable login Module


Step2 : Plug it in your custom auth handler



Example: https://svn.apache.org/repos/asf/sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/

Example of Open Source Extended Authentication Handler:



CQ OOTB Extended authentication Handler

Day CRX Sling - Token Authenticationcom.day.crx.sling.crx-auth-token)Adobe Granite SSO Authentication Handlercom.adobe.granite.auth.sso)Day Communique 5 PIN Authentication Handlercom.day.cq.cq-pinauthhandler

There are a lot of things needed for creating your custom user registration process (You might / Might not) Need following,

1) Custom Login Module http://www.wemblog.com/2012/06/how-to-add-custom-login-module-in-cq55.html to sync users / group in CQ from third party system
2) Custom Authentication handler as above
3) Reverse replication to sync user across (If user registration is in publish)

Note: Above code is just Pseudo code. Please test, You might have to add your custom logic for this to work.

Let me know if you have any question or comment.