Showing posts with label ALUI Publisher. Show all posts
Showing posts with label ALUI Publisher. Show all posts

Tuesday, November 17, 2009

Deconstructing Publisher (6.4) Rich Text Editor and Fixing the Adaptive Tag Reformatting Bug

Stating the Problem

Have you ever noticed that the Publisher Rich Text Editor (RTE) only supports a couple of Adaptive Tags: Current Time, Page Name, Community Name, and User Profiles…

I am not saying that only because no other tags show in the RTE “Adaptive Tag” button…(which is already kind of weird, but would not be so bad compared to what’s coming), but because if you want to use any other tag in the RTE source view, switching to the design view will completely mess up your tag…

Here is the example: I want to create a link to a portal page within my RTE content (no, not a common task at all)…and because I like the Adaptive Tags, I sure should use the pt:standard.openerlink tag, as follow:

<pt:standard.openerlink xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/' pt:objectid='426' pt:classid='512' pt:mode='2'>A link To Portal Page</pt:standard.openerlink>


Great, I enter that in my Publisher RTE field, in the HTML source view…and going back to the RTE edit view will tranform automatically my nicely formatted (and working) tag into something useless:



<standard.openerlink pt:mode="2" pt:classid="512" pt:objectid="426" xmlns:pt="http://www.plumtree.com/xmlschemas/ptui/">A link To Portal Page</standard.openerlink>


As you can see, the RTE design view “ate” the “pt:” and messed up the closing tag as well…



Ultimately, the portal will not understand that tag anymore…and your hope to create a nice link to page using best practice Adaptive Tags is just not realistic so far.



This bug is crazy to me…I would have better understood if the above problem would be happening only with the custom tags you wrote yourself – yes, it obviously happens also with any custom tags you wrote –, but it is not even working with the default “Out of Box” ALUI Tags…I just don’t know how this could have passed QA…oh well…



Apparently that bug is fixed with Publisher 6.5 (check out the release notes), which I believe uses a completely new Rich Text Editor…But I have not personally verified if Custom Tags (the ones you or I wrote) are also working fine with the RTE in Publisher 6.5…,to be continued on this. (Let me know if you verified this)



Either way, if you are not ready to move to Publisher 6.5 quite yet, well, you have 2 options:




  • Try to use a better RTE instead of the one Publisher provides (this might be a good idea, although it has caveats too, such as uploading files in publisher, or create links to other publisher items)


  • Continue to read this post, and implement my fix.



Understanding the Why



After a couple of hours of reverse engineering (thanks to Visual Studio 2008 awesome JavaScript debugging capabilities), I finally found a fix for this issue…



But first, let me explain briefly how it all works…




  1. The publisher Servlet in charge of drawing the content item entry screen (java code) output a huge XML piece within the HTML source of the page…This XML will be the base info that will be read and transformed into Javascript objects, which will be used by the RTE buttons and various other behaviors.


  2. Part of the XML, we can see a pretty interesting one (very related to our problem): “replacementToken”



    <replacementToken index="1" class="PTRichTextToken">
    <pattern>(&amp;lt;PT:PAGENAME[^&amp;gt;]*/&amp;gt;)|(&amp;lt;PT:PAGENAME[^&amp;gt;]*&amp;gt;(.*?)&amp;lt;/PT:PAGENAME\s*&amp;gt;)</pattern>
    <replacementHTML>Page Name</replacementHTML>
    <style>background-color:#FFFF33;border:1px dashed #B9B933;</style>
    <tooltip>Name of the current portal page</tooltip>
    </replacementToken>



  3. As you can see above, the replacementToken contains a regex pattern, a replacement name, a style, and tooltip…This “token” (or more rightly, its existence in the page source) is exactly the cause that makes the only 4 ALUI Adaptive tags (Current Time, Page Name, Community Name, and User Profiles) work…

    Basically, what happens when you toggle between “Design View” and “Source View” is the following:


    1. In source view, you enter your Adaptive tag the way you should, nicely formatted the way the documentation says…


    2. When you toggle to design view, each replacement token regex pattern is executed in order to find if there are indeed any pttag in the source…


    3. Each positive find is first saved into a Javascript hashmap, and then is transformed into a proper valid HTML tag: <span>


    4. So really, in design view, the tag you entered or inserted is just a <SPAN></SPAN> tag, with a particular ID and style as defined by the token…

      Hence, for example, the pagename tag would really be in the design view something like:

      <span id=”PTRichTextToken1” style=”background-color:#FFFF33;border:1px dashed #B9B933;”>Page Name</span>

      This is why you see the dashed yellow line when you add the pagename tag using the RTE button…


    5. And if you toggle back to the source view, the replacement happens again in order to re-output the original tag you entered (using the javascript hashmap filled in step 3)





Ok, so this is overall the way RTE Publisher 6.4 is working with Adaptive Tags… Now we understand this, we need to implement our fix, right?



Delivering the How



Really, the fix is easy…and if I had the publisher java code, it would simply be a 5 minute job…All we would need to do is add all the new tokens you need (with proper regex pattern  for each one, etc…) in the Java class in charge of outputting the XML…



Well, I don’t have the code…



So the 2nd option is hacking the Javascript in order to add the new replacement tokens you want to the already built array of Replacement Tokens…simple, right?



The file that contains all these replacement mechanisms is PTControls.js (found in \imageserver\plumtree\common\private\js\jscontrols\)…and that’s the one we will modify a bit…



First, simply add the following method in there…I added it just before the method "PTRichTextControl.prototype.init" (approx line 5178) because we will need to modify that method too…



//customization: fabien sanglier
PTRichTextControl.prototype.AddCustomReplacementToken = function()
{
//make sure customtokens array have same size as tokenDecorators array...they work hand in hand.
var customtokens = new Array(
new PTRichTextToken('(<pt:customtaglib.openerlink[^>]*/>)|(<pt:customtaglib.openerlink[^>]*>(.*?)</pt:customtaglib.openerlink\s*>)','$3 (OpenerLink)'),
new PTRichTextToken('(<pt:standard.openerlink[^>]*/>)|(<pt:standard.openerlink[^>]*>(.*?)</pt:standard.openerlink\s*>)','$3 (Standard OpenerLink)')
);

//make sure tokenDecorators array have same size as customtokens array...they work hand in hand.
var tokenDecorators = new Array(
new Array('background-color:#FFFF33;border:1px dashed #B9B933;','Opener link to any objects in the portal'),
new Array('background-color:#FFFF33;border:1px dashed #B9B933;','Standard Opener link to any objects in the portal')
);

if(this.replacementTokens && this.replacementTokens.length > 0){
if(customtokens && customtokens.length > 0){
var startindex = this.replacementTokens.length;
var addDecorators = (tokenDecorators && customtokens.length == tokenDecorators.length);
for(var i = 0; i < customtokens.length; i++){
this.replacementTokens[startindex + i] = customtokens[i];
if(addDecorators){
this.replacementTokens[startindex + i].style = tokenDecorators[i][0];
this.replacementTokens[startindex + i].tooltip = tokenDecorators[i][1];
}
}
}
}
}


If you read carefully the above method, you’ll see 2 arrays: customtokens and tokenDecorators…by adding new items to these 2 arrays (which work together), you can add support for as many tags (out of the box or custom) as you want.



Next, add 1 line to the PTRichTextControl.prototype.init, which simply calls our new method above:



PTRichTextControl.prototype.init = function()
{
...

//begin customization: fabien sanglier: add the extra array for the custom tags
this.AddCustomReplacementToken();
//end customization

PTControls.makeGlobalObject(this,this.objName,true);
}


Not too bad to fix such a bad bug, right?



Note1: Make sure you clear your browser cache when you test! You might still get the old un-customized javascript…



Note2: this is non supported…make backup of original file and do it at your own risk bla bla bla usual stuff bla bla bla



Final words



Why haven’t they done this earlier? go figure!



Is it annoying to have to do this so that out of the box features works within out of the box product? Definitely!!



Was it interesting to understand this whole mess? Absolutely!



So long…

Sunday, March 15, 2009

ALUI Publisher: Easy bug fix with portlet templates…

I know ALUI Publisher is on the down slope, but until then, it does not mean I should not share some of my findings…Lately, I found something in publisher that does not make sense to me. It has to do with the creation of portlets based on publisher portlet templates. Let me explain the problem…and then the quick fix…

The problem:

Out of the box, when you create a portlet based on a publisher portlet template (i.e. announcement, news, etc…), there is a screen where you are asked to choose a publisher folder where the portlet publisher content should be…When clicking on the “chose publisher folder”, a publisher tree pops up, and you can pick the publisher folder. The tree picker will only show you the folders where you have “producer” access…until now it makes sense since only the “producer” role and above can create folders in publisher.

But what if a user does have “producer” access to a subfolder Z located in the tree structure at X > Y > Z…but does not have producer access to the parent folder X and Y?? Then the tree picker would simply stop showing the tree at X, hence not showing the subfolder to which the user has actually access to…hence problem.

A perfect (and probably common) use case is where you have various publisher sites (i.e. an intranet site, and internet site, etc…) and within each of these, you have various “community-related” publisher folders… (folders that contain the web content of each community). You will want your community administrators (in the portal) to also have “producer” or “folder administrator” role in their “community-related” publisher folder…but not have these roles in the parent publisher folders…they should have “reader” access on these parent folders…

The solution:

By reverse engineering publisher one more time, I found out that this problem can be fixed really easily…basically the publisher tree picker is opened with the following url:

“../folderpicker_frame.jsp?sid="+sessionId+"&showItemCategory=0&itemId="+parentFolderId+"&isMultiSelect=false&rootIsCheckable=true&minRoleId=12”

What is interesting in this url is the last parameter: minRoleId=12. What it probably means is “show only the folders to the users who have at least a role 12 – producer role – assigned to it.” That’s it, we have our solution…By removing this parameter altogether, the tree will now show the folders to which the user has minimum access to (“reader” access), hence fixing the user case explained above:

A producer in folder X > Y > Z will now be able to browse down to the Z folder, where he will be able to create a folder.

And don’t worry, it does not impact core security at all. If the user tries to select folder X or Y as a container for his “announcement” portlet, he will simply receive an error message saying ”you do not have enough access to create a folder”…so no problem.

Detailed instructions:

  1. Make a backup of the publisher application files \bea\alui\ptcs\6.4\webapp\ptcs.ear and \bea\alui\ptcs\6.4\webapp\ptcs.war (obvious, no?)
  2. Unpack the publisher archive (ptcs.war)
    1. navigate to \bea\alui\ptcs\6.4\webapp folder
    2. create a new dir: ptcs
    3. navigate to that new dir and execute the following jar command: jar –xvf ../ptcs.war
  3. edit the extracted file: ./portlet_packages/portlet_create.jsi
  4. Find the JavaScript functions “ChooseParentFolder” and “NewParentFolder”
  5. Remove the “minRoleId=12” from the “var url = …” line (1st line in each functions)
  6. Still in \bea\alui\ptcs\6.4\webapp\ptcs folder, repackage the war by executing: jar -cvf ptcs.war *
  7. move the newly created war to the \bea\alui\ptcs\6.4\webapp folder: move ptcs.war ../
  8. navigate to \bea\alui\ptcs\6.4\webapp: cd ../
  9. update the ptcs.ear file with the following command: jar -uvf ptcs.ear ptcs.war

That’s it…

Before restarting publisher, clean the temp files from the publisher container folders just to make sure your modification is loaded properly.

  • \ptcs\6.4\container\tmp\deploy
  • \ptcs\6.4\container\work\jboss.web\localhost

As usual, do this at your own risk…and test it well before deploying to production :)

Monday, October 27, 2008

ALUI Publisher - Part 3: No Redirect Bug Fix of Bug Fix :)

I have been relatively lazy in regards of my blog lately, and I have plenty of articles that are stacking up...But in the meantime, I thought this one is pretty urgent.

In one of my previous article, ALUI Publisher - Part 2: Increase Performance by enabling REAL Caching - No Redirect Bug Fix, I was explaining how to fix the Publisher published_content_noredirect.jsp (see  in that serie the benefits of using this instead of published_content_redirect.jsp).

Well, I found out a small little bug on my part, and it is definitely worth fixing if you have not done so already. In my corrective code, I was trimming the content string from its end spaces (just to optimized the HTML output)...and I did not think of the likely negative effects, such as if the buffered content does finish on a meaningful space character (such as a sentence separator etc...)

So the updated code with bug fix is: (specifically the trim that is removed)

//if there is content, forward to the requesting client
int buffersize = 2000;
int charread = 0;
char[] content;

//read until no byte is found in the input stream because request content length is no reliable
// UTF-8 is necessary
BufferedReader bisr = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

do {
content = new char[buffersize];
charread = bisr.read(content);
out.write(new String(content).trim());
} while(charread > -1);

bisr.close();
bisr = null;
content = null;


Better to find it late than never! :)

The code should be updated on the famous (yeah right...) ALUI Toolbox Google project (or should be soon)

So Long.

Tuesday, July 15, 2008

ALUI Tool: URL (or text) Migration within Publisher Items

Following my previous article "ALUI Administration Tool for Environment Refresh: String Replacing for URLS" talking about migration between environments, here is an extra piece that you might find very useful (I surely use it all the time)

Basically, as explained in the previous article, it is common to have different DNS aliases set up per environment...I.e for publisher remote server, you could have:

Similarly, the publish browsing URL is not an exception to this rule:

  • http://publisher-content.domain.com/publish  for production
  • http://publisher-content-stg.domain.com/publish for staging
  • http://publisher-content-dev.domain.com/publish for development
  • When you add an image or a link in the free text editor of content items in publisher, it will most of the time create an absolute URL to that resource...thus you can imagine that there will be a lot of DNS aliases within a lot of publisher items throughout the environment.

    What happen when you migrate the publisher DB from one environment to another? Well you will have a lot of DEV dns aliases within your Staging environment (in case of a DEV promotion to Stage); or a lot of production DNS aliases within your dev environment in the case of a production refresh to DEV.

    In my previous article "ALUI Administration Tool for Environment Refresh: String Replacing for URLS", I was mostly talking about migrating URL within portal objects, but nothing really about migrating urls within publisher items.

    Thus, I created some DB scripts (SQL Server only for now) that do just that...

    1. puburls-PTCSDIRECTORY-nvarchar-replace.sql: Script to change a particular string within the PUBLISHEDTRANSFERURL and PUBLISHEDURL columns (which is mapped out in the DB to a column of type VARCHAR)
    2. puburls-PTCSVALUE-ntext-replace.sql and puburls-PTCSVALUEA-ntext-replace.sql: Scripts to change a particular string within the "long text" property of a publisher item (which is mapped out in the DB to a column of type TEXT)
      1. PCSVALUES.LONGVALUE (hosting the long text of the currently published item)
      2. PCSVALUESA.LONGVALUE (hosting the long text values of all the previous versions of the item)

    For the first script, PUBLISHEDTRANSFERURL and PUBLISHEDURL columns are of type VARCHAR and thus it is easy to replace a string within those columns using the REPLACE MS SQL Function. Thus, a simple SQL statement is good here.

    The main challenge was really with the 2nd scripts...indeed, within a column of type TEXT, the SQL "REPLACE" function cannot be used...The workaround is to use the PATINDEX and UPDATETEXT functions within a Transact-SQL (T-SQL) script. To give the credit to to the right person, I adapted a script that I found at ASP FAQ - How do I handle REPLACE() within an NTEXT column in SQL Server?

    DISCLAIMER: ALTHOUGH I PERSONNALY USE THIS SCRIPT ALL THE TIME, THERE IS NO GUARANTY; SO USE THIS TOOL AT YOUR OWN RISK blah blah blah AND USE IT ONLY IF YOU ARE PROFFICIENT ENOUGH WITH ALUI PORTAL TECHNOLOGIES.

    Attached is the zip file package that contains the 3 scripts:

    Don't forget to change the string to look for, and the string to replace it with

    puburls-PTCSDIRECTORY-nvarchar-replace.sql

    UPDATE [dbo].[PCSDIRECTORY]
    SET
    [PUBLISHEDTRANSFERURL]=REPLACE([PUBLISHEDTRANSFERURL],'-DEV.DOMAIN.COM','-TST.DOMAIN.COM'),
    [PUBLISHEDURL]=REPLACE([PUBLISHEDURL],'-DEV.DOMAIN.COM','-TST.DOMAIN.COM')
    WHERE
    publishedtransferurl like '%-DEV.DOMAIN.COM%'
    or publishedurl like '%-DEV.DOMAIN.COM%'



    puburls-PTCSVALUE-ntext-replace.sql and puburls-PTCSVALUEA-ntext-replace.sql



    SET @oldString = N'por-pubcontent-dev.domain.com'; -- remove N 
    SET @newString = N'por-pubcontent-tst.domain.com'; -- remove N


    That's it! Let me know if you find it as useful as I do! Enjoy!!

    Monday, June 30, 2008

    ALUI Publisher - Part 2: Increase Performance by enabling REAL Caching - No Redirect Bug Fix

    Previously (http://fsanglier.blogspot.com/2008/06/alui-publisher-part-2-increase.html) , I've been talking (and proving I hope) that using a "no redirect" mechanism for serving published content from publisher is the best option to enable portal caching. Publisher 6.4 offers already such a possibility (although not publicized a lot): using in the portal Published Content Web Service object published_content_noredirect.jsp instead of the standard published_content_redirect.jsp.

    Unfortunately, if you start using this, you are going to start seeing a weird behavior: the publish content is getting truncated in some special cases...and this is due to the way the JSP has been coded. Several options for you: either you wait for a Critical fix to be issued to you by BEA (i am not aware of one yet), or you upgrade to ALUI 6.5 (I hear that this has been fixed in 6.5...have not verified though), or you simply do it yourself, as this is a simple fix to implement (ultimately, that might be the same type of code that would be issued by a CF I imagine)

    By looking at the JSP within the publisher web application archive (ptcs.war - explode the war using jar command), we can see what's wrong and why the content is truncated in some case:

    HttpURLConnection conn = (HttpURLConnection)url.openConnection();

    // make the request
    conn.connect();

    //read the content length
    int contentLength = conn.getContentLength();

    //if there is content, forward to the requesting client
    if( contentLength > 0 ){
    // UTF-8 is necessary
    InputStreamReader isr = new InputStreamReader(conn.getInputStream(), "UTF-8");
    char[] content = new char[contentLength];
    isr.read(content);
    isr.close();
    out.write(content);
    }


    As you can see, an HTTP GET request is made, and the content length of the response is gotten from the "getContentLength()" method. This call is going to get the content length number fro mthe response header rather than actually count all the bytes that are contained in the response content. Thus, since the code base itself on this number to output the content to the JSP output stream (see above: char array of length equal to contentlength), the content will indeed be truncated if the contentlength number is not correct...



    A simple correction (and more robust code) is actually to make sure ALL the content is pushed to the output stream, independently from the contentlength number returned by the response header. Here is my code below that fixes that issue, and also increase performance by using the preferred BufferedReader wrapper class instead of the bare InputStreamReader:




    ------EDITED 3/12/2009--------
    BufferedReader bisr = null;
    try {
    bisr = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
    String line;
    while ( (line = bisr.readLine( ) ) != null ) {
    out.println(line);
    }
    }
    catch(Exception exc){
    throw exc; //to be caught by the global try catch
    } finally {
    if(bisr != null)
    bisr.close();
    bisr = null;
    }
    return;
    ------END EDITED 3/12/2009--------

    Basically, the code will read by chunks of 2000 chars (this is a chunk size that I think is appropriate) the entirety of the content until the last character...and write it all to the output stream...This does not rely on the contentlength at all, and thus is more reliable and robust.



    After changing the published_content_noredirect.jsp as above, you can repackage the ptcs.war with the new corrected JSP (within the root of the previously extracted ptcs.war folder, run jar -cvf ptcs.war * command) and redeploy to ALL redirector and publisher instances...



    Voila, you have your perfect solution for ALUI 6.1 and Publisher 6.4 (and previous versions too).

    ALUI Publisher - Part 2: Increase Performance by enabling REAL Caching

    In my previous post http://fsanglier.blogspot.com/2008/02/alui-publisher-increase-performance.html(man, already couple of month ago...I know I've been sucked into a black hole since then :) ) I was talking about how to best design a scalable and redundant ALUI Publisher architecture. But what I had not pushed enough in the last article was performance.

    In my opinion, the Publisher standard behavior for serving content has some performance flaw (at least in ALUI 6.1 - Publisher 6.4) related to caching... Since there are ways around this (that's what we do, right?), I think you might benefit from this a lot, and in my last implementation, the change explained below increased performance under load to complete new levels (in the meantime reducing DB requests and DB and publisher redirector CPU utilization)...So here it goes...

    As I explained in previous post, each published content portlet within a portal page are going to make a request to the Publish Content Redirector component, and particularly the JSP page in charge of performing the redirect: published_content_redirect.jsp (you can see that defined in the Published Content portal web service object). As the name says, this Java Server Page (JSP) performs a 302 redirect to the published content item (as seen in previous post, this location should be served by your favorite web server, apache or IIS for example...). But BEFORE making this redirect, it must know where to redirect to... so for that, the code makes a DB request to the publisher Database in order to get the browsing path to the published content item (passing the publisher content item ID that was saved when you created your published content portlet earlier).

    Ok so here is the first flaw I was taking about in previous paragraph: To be able to see some content through the publisher portlets within the portal page, you can see from the above explanation that multiple calls to the publisher DB will be made. Let's say we have 5 publisher portlets on the page (not uncommon), for each page rendering for 1 user, 5 DB calls will be made to the publisher DB (in addition of multiple other DB calls for portal and analytics). If we are now talking about thousands of users, we are talking about too many DB calls to simply see some content that does not change often. This has an unnecessary impact on DB load of your infrastructure, and will increase the page load consequently since DB calls are inherently slower that simple content rendering...

    While reading, some of view are already thinking for a good reason: CACHING! Yeah, indeed, caching is the secret to scaling and performance (not always necessary to take out the big bucks and supercharge even more the DB infrastructure). And great for us, the portal offers a great out-of-the-box caching capability within the web service object: simply set the minimum caching to 2 hours, max to 20 days, and normally, you would think that the portal should simply cache the published content for that amount of time...removing the need to call the published_content_redirect.jsp altogether, and thus the need to make a DB call!! ...but it does not happen this way. You don't believe me? Enable access logging on publish content redirector components (un-comment "Access logger" section within <ALUI HOME>\ptcs\6.4\container\deploy\jbossweb-tomcat50.sar\server.xml) and you will clearly see that even though your page contains only published content portlets that should be cached, the published_content_redirect.jsp  is still constantly called...and thus caching is not really....caching.

    Why does this happen? It is because of the redirect mechanism for serving content...From www.w3.org, 302 is explained this way: "The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field." Thus, the portal is doing is job perfectly (it acts as a client here) and will never cache a response with a temporary redirect 302 status code.

    Ok so what can make it better? Changing the redirect mechanism to something that does not redirect...Well, using this other JSP actually already present and developed within ALUI 6.4 (I have not verified if present in earlier versions of publisher): published_content_noredirect.jsp. Instead of issuing a redirect to the published content, this jsp performs an HTTPGetRequest to it and write its content to the JSP output stream. The response code is now a simple 200 OK ("The request has succeeded" - www.w3.org) that can be cached by the portal. To enable this, simply change the HTTP url of the Published Content Web Service Object to be published_content_noredirect.jsp instead of published_content_redirect.jsp. Of course, check out the publisher redirector access logs to see the dramatic difference...under load, you will initially see a bunch of requests to published_content_noredirect.jsp, but very very fast, the access log becomes silent, all the content being really cached by portal...

    Result?? You can increase the load even more, and the page response satys the same (or is even better), and the DB utilization is not altered by that load...you simply have a site that is so much more performant. Our initial results showed that under constant intensive load (with and without the change) the publisher infrastructure would not crash anymore, the CPU usage of both DB and publisher redirectors would be consequently diminished, the number of DB requests would drop, and the load could actually be increased to new levels without loss of performance and functionality, thanks to caching.

    Since this post is already long, I am going to stop here for now...Please read next post (http://fsanglier.blogspot.com/2008/06/alui-publisher-part-2-increase_30.html) to understand the second flaw: the published_content_noredirect.jsp has a truncation bug...that is fixable of course :)

    So long!

    Tuesday, February 26, 2008

    ALUI Publisher: Increase Performance, Scalability and Availability

    ALUI publisher is the component that provides out of the box Web Content Management for the ALUI portal. Depending on the nature of your portal, Publisher can be an important or even critical part of your portal infrastructure (ALUI-powered Internet sites for example, or intranet with a lot of static content displayed through portlets).

    So if this component is really critical, the architecture design for that component must provide performance, scalability and redundancy. This article is meant to show you how to design Publisher Architecture to maximize performance, scalability and redundancy, and ultimately provide the best experience to the end user.

    Published Content portlets - How it works:

    First, let's lay out all the key components and understand the request flow for each Publish Content Portlet on a single portal page:

       PubContentRequestFlow_thumb_1

    It is important to reiterate that this flow is not per portal page, but per portlet. In other words, a portal page with 4 published content portlets will initiate 4 simultaneous request flows like the one above. From that, you can understand that depending on the load of these portal pages, the number of requests to the redirector and the publish content services could easily skyrocket, which is a strong proponent of clustering these components.

    The redirector, as its name states, will simply redirect the request to the right content url, based on the content ID that is given. Since this component actually makes more intensive DB calls and is accessed for every pub content requests (when content is not cached by the portlet of course), we definitely would want to cluster it and allow for future addition to that cluster when the load increase.

    The published content piece is a very simple "static web content serving solution". Although JBOSS or any other application servers are able to serve static content, none are as performant as true web servers optimized for the task: Amongst others, Apache or IIS are good possibilities.

    The problems:

    The main problem consist in the fact that the installer does not provide any tip to help you achieve that. By default, Publisher installer provides 2 options: Full Component (it means publisher engine, workflow engine, and the embedded redirector) or Publish Content Redirector Only.

    A couple of common shortfall if out of box design is followed:

    • Publisher engine (explorer) cannot be clustered.
    • Publisher internal application server (JBOSS) is serving publisher, workflow, redirection, and published content.
    • Publish Content Redirector standalone is not optimized for serving static content (it is used mainly to redirect to the actual publish content url, based on the requested publisher item ID)

    The Solutions:

    Ultimately, what we want to do is decouple each component from each other in order to scale it independently from the others. Thus, what we want to improve through architecture design consist in:

    • Publisher and workflow engine set up in failover mode. (Hot-Warm or Hot-Cold failover)
    • Publish Content Redirector Load balanced using Hardware (F5 BigIP, Cisco LB) or software load balancer.
    • Publish Content Served by appropriate web server (apache or IIS), optimized to best deliver static content (best performance)...instead of the publisher internal jboss app server (better optimized for dynamic J2EE content delivery).
    • Publish Content Web Server (apache or IIS) to be load balanced using Hardware (F5 BigIP, Cisco LB) or software load balancer.

    Following that design, the redirector can be scaled independently from the publish content serving mechanism, as well as from the publisher admin. And also, if the server that hosts publisher is down, the redirector services and publish content services are still able to provide content without any problem. Similarly, since the redirectors and pub content web servers are load balanced, they are fully redundant, providing maximum availability of static content on your ALUI powered internet site for example.

    So how do we tie everything together? Here is an overview of tasks that corresponds to what we talked about until now: (here using 4 servers in my example...could use more or less depending on what hardware is available to you...with a minimum of 2 servers of course.)

    1. Install full Publisher on Server A (contains redirector capability by default)
    2. Import the publisher objects in the portal (pte file)
    3. Install redirector component on Server B
    4. Install Apache/IIS on server C and D
    5. Create the 3 load balancing pools, 1 for Publisher (useful for HOT-WARM failover), 1 for Redirector, 1 for publish content web server.
    6. In publisher explorer, set the "publishing target" to a fault tolerant and fully redundant file share. (clustered NAS for example)
    7. Still in "publishing target" screen, set the browsing url to make sure it access the publish content web server pool (and as such access the Apache/IIS web servers)
    8. Setup Apache/IIS web servers on C and D in order to expose the published content that is at the "publishing target" set up previously
    9. We need to modify the publisher portal objects in order to make them access the correct load balancing pools
      1. Change PubContent Web Service Url to make it access the redirector load balancing pool.
      2. Also, change accordingly the gateway prefixes of the PubContent Web Service, especially the ones related to published content (publish/preview)
      3. Make sure the publisher url is actually going to the LB instead of going directly to publisher.

    One last thing you might want to do to still increase performance: Make sure you publish the images to the image server instead of the publish content repository. (Since the image server is publicly accessible from the internet, do this only if the images you are working with are not exposing information that should not be publicly accessible!). This is explained by the fact that serving binary content through the portal gateway component is much slower than serving the content directly to end user.

    Et Voila! When you are done configuring all that, you have a publisher infrastructure that is completely redundant in every aspects, offers maximum scalability for each critical components, as well as  maximum performance for serving static content.