First, let me apologize for my lack of posting for the last month and a half. I have no excuses. I have been lazy. That said, this should be a pretty interesting post...
Thus far, this blog has focused on fairly technical analysis and deconstruction of the ALUI product stack -- analysis which carries with it plenty of disclaimers, but should at least help the reader understand how the product works. This post is no exception -- in fact, this post will carry with it my biggest disclaimer yet:
Proceed at your own risk. What I am about to do is neither supported nor advocated by anyone at BEA. Not even me. This could break the product, make you a social outcast, even prevent you from playing on the company softball team. You have been warned.
Okay. Still with me anyway? Now that you're proceeding after ignoring my bolded advice above... how about being able to add your own custom JSP's to manipulate Publisher? And how about I start by explaining why you might think that's a good idea.
An Example: Edit Inline
Every good analysis needs an objective. In this instance, I'm going to pick a depreciated capability in Publisher 6.4, the ability to edit inline in a portlet. You generally see this capability in the Announcement Portlet Template, but it sure would be nice to be able to turn it on or off for all portlets/portlet templates, or even possibly fix portlets that don't have it turned on, for some reason. As you can see from the screenshot below, that's currently not possible in the Publisher UI:
Figure 1 - Edit Inline Capability in the Announcement Portlet Editor
How about we change that?
Tools You'll Need to Follow Along
As with any adventure, we won't get far without the right tools. In order to keep with this post, you're going to need the following:
WinRAR - I love this product. It'll extract any zip/rar/jar/war file and preserve folder structure, unlike WinZIP. Even better, it's cheap, easy to use, and has great Windows Explorer extensions. You'll never use WinZIP once you use WinRAR.
TextPad - You don't necessarily need a great text editor for this exercise, but having color coded text files is a life saver. I personally use this to open JSP files, so I'm not burdened by the overhead of a full-fledged IDE. This is my editor of choice.
Eclipse - You're going to need a Java IDE, whether you like it or not. There are other IDE's available, but Eclipse is free and one of the most popular. Plus, you'll need to use it in conjunction with the below...
Jad - Jad is a java decompiler. You may need this in some instances to figure out what is going on. It's a very simple command line executable that you can put in a folder somewhere and create an environment variable for.
Jadclipse - I cannot tell you how many times I have thanked my lucky stars that this product exists. It's an Eclipse extension that allows you to decompile java classes on the fly in the Eclipse IDE, which means you can right click any object in your source code, choose Open Declaration and view Java source.
I'm not going to teach you how to use these tools, nor point out their possible mis-uses, since there are a bajillion internet resources for that. I'm going to assume you have rudimentary knowledge enough to keep up. If you don't, then you probably shouldn't be doing this anyway =).
Deconstructing Publisher
The important thing to keep in mind is that Publisher itself is just a Java web application being hosted in a customized version of Tomcat. As such, we can do anything to it that we could do to any other custom web application, including: extracting files from the war file, analyzing the libraries, and adding our own JSP's to the war.
It turns out the adding JSP's part is the simplest portion of this exercise. All we have to do is open up the war file using WinRAR and drop them into the folder structure. But I'm getting ahead of myself. First, we have to figure out how we might go about writing a JSP that would change the Edit Inline capability. Let's start by taking a look at the main web application WAR file, found at:
$PT_HOME/ptcs/6.4/webapp/ptcs.war
As I mentioned, we can open this file in WinRAR, since it is compressed in the same manner ZIP files are. I prefer to just extract all of the files and folders to a directory, since we will need some of them later. The base directory should look something like this:
Next, I set up a quick workspace in Eclipse with a single Java project in it (make sure JadClipse is installed in this workspace). I then create a User Library: Window | Preferences -> Java | Build Path | User Libraries| New...
Usually, I call this library Publisher and I use the Add Jar's button to add all of the jar files in the WEB-INF/lib directory from the above ptcs.war file.
Once this has been done, I create one more User Library called Tomcat which contains all of the jar files in:
$PT_HOME/common/container/tomcat/5.0.28/common/lib
I then add both of these libraries to my Java Project's build path. At this point, we have referenced all of the libraries needed to build Publisher code in our Java project.
Astute readers will probably note that there are better ways (especially in Eclipse) to compile and test JSP's, but I am a simple man, and I find this works fine for my simple purposes.
Finding a Starting Point
Once we have built an environment, we need to figure out how to get Publisher to do what we want it to do. To me, this means finding a starting point for learning about how the product works. In my case, I happen to have worked with the product for quite a while, so this isn't particularly tough. In cases where you're not following my example, you will have to dig around the JSP's you find in the ptcs.war and try to make some sense of them.
In the case of Edit Inline, the place I started was [ptcs.war]/published_tools/content_item.jsp. Why start here? Well, I happen to know that this JSP is used by the News Portlet to do simple things like publish articles and create new ones. I also happen to know it's not that complicated in its mechanics, so it should be pretty easy to figure out.
Looking at the JSP, it turns out there's not much for us to see. The bulk of the heavy lifting done by the Publisher product is actually done in a bunch of java libraries contained in the WEB-INF/lib directory in the war (see the import statement near the top of the JSP for the packages which are used).
Building Our Own Library
I suppose there's not much mystery here, either. The easiest way to manipulate functionality in the product is to build our own library that does whatever we want, then insert it into the war file (this can be done simply by packaging our custom code in a .jar file and shoving the jar file into the WEB-INF/lib directory of the war file.
In my case, I want to build a page that will let me turn Edit Inline on and off for a particular portlet. To that end, I am going to create a static function that will automatically read (1) a portlet ID and (2) whether or not 'edit inline' is being turned on or off, from query string parameters on the URL. I will then write a simple JSP which calls that function when it is hit by a browser.
In order to make sure I was correctly instantiating a Publisher session and reading parameters, I used the code in com.plumtree.content.client.http.content_gadget.ContentGadgetTools as a model, then just extrapolated the Edit Inline part by spelunking the libraries and testing on my local Publisher instance. Since I wanted to keep it simple, this code sets Edit Inline only for the first content item in a portlet. It could be modified if other results were desired. Here is what I finally came up with:
package com.plumtree.content.client.http.content_gadget;
import com.plumtree.content.*;
import com.plumtree.content.app.PortletConfigEditor;
import com.plumtree.content.app.Session;
import com.plumtree.content.app.User;
import com.plumtree.content.client.http.*;
import com.plumtree.content.data.*;
import com.plumtree.content.util.portal.PortletId;
import com.plumtree.remote.portlet.*;
import javax.servlet.http.*;
public class RossContentGadgetTools {
public static Session getSession(HttpServletRequest request,
HttpServletResponse response) throws ContentException {
// initialize a publisher session using the HttpClient object and portlet context
ContentSystem.startup();
HttpClient.initializePage(request, response);
IPortletContext portletContext = PortletContextFactory
.createPortletContext(request, response);
// here's our session...
String userName = portletContext.getUser().getUserName();
Session session = null;
String sessionId = null;
sessionId = (String) request.getSession().getAttribute("sid");
if (sessionId != null)
try {
session = Session.get(sessionId);
} catch (DoesNotExistException e) {
sessionId = null;
}
if (session == null) {
int userId = portletContext.getUser().getUserID();
session = User.login(userId, userName);
request.getSession().setAttribute("sid", session.getId());
}
// return the session
return session;
}
/***
* Set edit inline for the first content item in a portlet
* @param request
* @param response
* @return
*/
public static String setInline(HttpServletRequest request,
HttpServletResponse response) {
try {
// get the portlet ID
int portletId = HttpClient
.getIntParameter(request, "portletId", -1);
boolean editInline = HttpClient.getBooleanParameter(request, "inline", true);
Session session = getSession(request, response);
PortletToolbox toolbox = session.getSite().getPortletToolbox();
PortletId pid = new PortletId(portletId);
PortletConfig portletConfig = toolbox.getPortletConfig(pid);
// instantiate the editor, set the first object to inline/not inline and
// save
PortletConfigEditor pcEditor = new PortletConfigEditor(session,
portletConfig);
pcEditor.confirmEditing();
// you could change the arguments here to something like 1,0, etc
// depending on the portlet and item
// (they could even be querystring arguments)
pcEditor.setInlineEditing(0, 0, editInline);
pcEditor.save();
pcEditor.unlock();
return "Success";
} catch (Exception ex) {
ex.printStackTrace();
return "An error occurred. Check the log for details.";
}
}
}
As you can see, all I really did was build a single custom functionality and package it in the same namespace as similar tools used by Publisher. In case you just want the completed library, here is a link to the jar file.
Leveraging Our Custom Library
Great. We have a function that appears to do what we want it to do. So now what? Now comes the easy part... we write a JSP that uses the function, then drop the JSP in our ptcs.war file. Here is what the JSP looks like. (view source to get a complete view) That's it. All I did was edit the war file using WinRAR and drag the update_inline.jsp into the top level directory.
Of course, there is one problem here: we have no direct link to the JSP in the portal. This means that our average user will never be able to get to the link (which is probably a good thing). But we know it exists, and we can use it. In order to change a portlet's Edit Inline setting, all you need to do is start up Publisher with the modifications mentioned, open the diagnostics page from Publisher Explorer, hit Control+N (this will give us a new window with a URL bar) and change the last part of the ptcs URL to point to the JSP with the query string arguments we require. For more detailed instructions, see the readme I wrote on how to install/use the customization.
You could also write an HTML page with a link to the JSP, all kinds of cool javascript options, etc, and create a portlet from the Publisher Web Service. Since I am lazy, I didn't do that.
Conclusion
First and foremost, read my disclaimer again. There is absolutely no support for doing what I describe in this post. Still... it's cool, it can lead to all kinds of neato functionality you could never hope to have without manipulating the product, and it will at least lead you to a much better understanding of what's going on under the covers.
Oh yeah, Happy New Year!