Showing posts with label Plumtree. Show all posts
Showing posts with label Plumtree. Show all posts

Wednesday, November 4, 2009

Oracle WCI: Warning: couldn't access subscriber X, continuing;

All of a sudden, our Saved Search Mailer scripts stopped working, after several years without a glitch.

The error I could find in the log files was this one:
Warning: couldn't access subscriber X, continuing
I spent the day trying to figure this out, as I couldn't find any clue using the PTSpy tool, nor in the million other log files... I had to decompile the Java classes of the "externaloperations.jar" library to find out what the process was doing before spitting out that error message.

Here is the code where that error message is shown:
void prepareSubscriberList()
{
int userIds[] = m_ptSearch.RetrieveSubscribedUsers();
if(userIds == null)
throw new RuntimeException("Saved Search object failed to retrieve subscribers");

IPTProfileManager ptPM = (IPTProfileManager)m_ptSession.OpenGlobalObject(58, false);
m_subscribers = new Vector();
for(int i = 0; i < userIds.length; i++)
{
int userId = userIds[i];
try
{
IPTUserInfo ptUI = ptPM.GetUserInfo(userId);
String email = ptUI.GetEmail();
if(email == null)
throw new RuntimeException("no email");

String name = ptUI.GetFullName();
if(name == null)
{
IPTUser ptU = (IPTUser)m_ptSession.GetUsers().Open(userId, false);
name = ptU.GetName();
if(name == null)
name = ptU.GetLoginName();
if(name == null)
name = "User";
}
if(ptUI != null)
m_subscribers.addElement(new Subscriber(userId, name, email));
}
catch(Exception e)
{
Log.println((new StringBuilder()).append("Warning: couldn't access subscriber ").append(userId).append(", continuing.").toString());
}
}
}

So, I guessed there was an issue with the email address received from the Remote API service. I checked the "User Profile Manager" admin tool, under "User Information - Property Map" and found that the "Email Address" property was mapped to the "IMUser" attribute, instead of the usual "Email" attribute. I changed it back to the normal value and everything returned to normal :-) This also "repaired" a lot of Collaboration Server errors, stating that the email address was not passed to the portlet.

I still have no idea why this value was changed...

Tuesday, September 22, 2009

Error in ALUI Collaboration Server when subscriptions contain deleted users

Recently, we encountered a problem when a user wanted to register other users in Collaboration Server for notifications. The error message was not very helpful, and I could not find a lot of info in collaboration.log nor in the PTSpy trace. The Oracle support helped me find the culprit: there was a discrepancy between the subscribed users in the CNS database, and the current users in the Portal DB. Apparently, when a user is deleted from the portal, there are items that remain in the collab and notification databases, and they cause Collab to crash...

To clean the leftovers in this case:

DELETE FROM SUBSCRIBEDACCOUNT WHERE SUBSCRIBERID NOT IN
(SELECT UUID FROM Plumtree.dbo.PTMIGRATION)


This script is run on the CNS database, Plumtree is the name of the portal DB, and dbo is the owner of the table.

Thursday, May 28, 2009

Caching portlets in ALUI 6.x

I've been developping portlets (gadgets) for nearly ten years now, starting with Plumtree Portal Server 3.5. A the time, there was a very nice document called "The Gadget Book" with all the details about the task of developping gadgets as they were called then. This PDF has been replaced by other versions since, but I still remember a few good practices for having a fast and reliable portal.

Among other things, there was the caching strategies to implement on the portlet side. The portal uses the standard HTTP mechanisms for calling content from the portlet server, as described in RFC 2616. Using the HTTP ETag and Last-Modified headers, we could prevent rendering a whole portlet when its content would remain unchanged. It proceeded like this:

- First call of the portlet by the portal. No special header is passed;
- The portlet returns the content to display, and sets the ETag and/or Last-Modified header;
- On the next access to the portlet, when the minimum cache time specified in the portlet configuration is over, and the maximum time not being reached, the portal calls the portlet giving back the content of the previous header;
- The portlet checks if the content has changed since the last call (in my case by comparing the timestamp passed in the Last-Modified header with a timestamp stored in a DB). If the content should be regenerated, I send the full content and give the new value for the headers. If not, I simply return an HTTP error 304 (Not Modified), and the portal would in that case display the content stored in its cache.

This worked perfectly for ages, until version 6.0 was released. On that version, when the 304 error was returned, the portal would display an error instead of displaying the cache (even when the setting "Suppress errors where possible (show cached content instead)" was checked.

I had several emails going back and forth with the Plumtree/BEA support and they finally acknowledge this as a bug. But in version 6.5, which is the one we are currently running (on http://www.myschool.lu/), that bug is still unresolved.

So, I disabled that part of my code, waiting for a solution to come eventually. I'm still waiting ;-) And my portlet is used in even more places than before (it displays content stored in a DB in many community pages) and the cache is key to allowing proper rendering times. The content would change once in a while, but hundreds of users would see the unchanged content in the meantime.

So, I tried to find a solution to this, and I have implemented the following workaround:

- Configure the portlet in this way (like in old times when caching worked):

- In my caching routine, I check the "CSP-Aggregation-Mode" header to see if I'm called as a portlet in a page, or as a standalone page (inside or outside of the gateway). This header can be empty (e.g. when called from outside of the gateway), can contain "Multiple" when displayed as a portlet in a page, or "Single" or "Hosted" when in an independent page accessed through the gateway.
- In the "Multiple" case, I do not return the 304 error but instead a Service Unavailable error (503). As per the setting above, the error is not displayed and the cached content is shown.
- In any other case, I return the 304 error, as a standalone page is properly processed by the browser. In such cases, the caching is done on the browser side and not the portal.

So far, so good. The performance has increased a lot, as could be expected, and the portlet server has more time to do other things than constantly rendering the same content...

Key lines of code:

Const c_sDateFormat As String = "yyyy-MM-dd HH:mm:ss"
Dim bFromPortal As Boolean = (Request.Headers("CSP-Aggregation-Mode") = "Multiple")

If dBrowserDate.ToString(c_sDateFormat) = dObjectDate.ToString(c_sDateFormat) Then
If bFromPortal Then
Response.StatusCode = System.Net.HttpStatusCode.ServiceUnavailable
Else
Response.StatusCode = System.Net.HttpStatusCode.NotModified
End If
Response.End()
End If

Response.Cache.SetCacheability(HttpCacheability.Public)
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches)
Response.Cache.SetLastModified(dObjectDate)