Blog

Liferay und Custom JSPs

Arbeitet man mit Liferay, so landet man bei Anpassungen von bestehenden Komponenten schnell bei so genannten Custom JSPs (Java Server Pages). Es gibt allerdings eine fiese Kleinigkeit zu beachten, wenn man seine eigenen Klassen in solchen JSPs referenzieren möchte.

Crash Course

Der einfachste Mechanismus zum Anpassen von Liferay ist ein Hook, der als Web Archive ausgeliefert wird und Custom JSPs beinhaltet,  die vom Namen her Originale von Liferay überschreiben. Zur Laufzeit werden nicht die originalen JSPs geladen, sondern die eigenen angepassten.

Szenario

Da das Anlegen eines Artikels in Liferay einen persönlicheren Touch bekommen soll (wir wünschen uns eine tolle Begrüßung beim Öffnen des Editors), brauchen wir einen Hook den wir „CoolArticleEditor-hook“ nennen. Der technische Aufwand hält sich in Grenzen. Man muss eine JSP anpassen und, da man den Begrüßungs-Code wiederverwenden möchte, eine Utility-Klasse schreiben.

/CoolArticleEditor-hook/ docroot/custom_jsps/html/portlet/journal/edit_article.jsp
(Hier wird nur das Delta aus Zeile 230 gelistet, denn die Datei ist zu groß.)

<h1><%= org.acme.liferay.journal.Util.getMeACoolH1(groupId) %></h1>

/CoolArticleEditor-hook/ docroot/WEB-INF/src/org/acme/liferay/journal/Util.java

package org.acme.liferay.journal;

import java.util.Random;

public class Util {

  private static final String[] HEADLINES = new String[] {
    "Hey dude!", "Yo, what up?!", "Nice seeing you."
};

private static final Random RANDOM = new Random();

public static String getMeACoolH1(long groupId) {
return HEADLINES[Math.abs(RANDOM.nextInt() * 31 * (int) groupId) % HEADLINES.length];
}
}

Erste Ergebnisse

Öffnet man den Editor nach dem Deployment, bekommt man zuerst einmal eine recht übersichtliche Seite präsentiert:

Zusätzlich winkt der Tomcat-Server mit folgendem Fehler im Log:

12:50:05,875 ERROR [IncludeTag:154] org.apache.jasper.JasperException: Unable to compile class for JSP:

An error occurred at line: 230 in the jsp file: /html/portlet/journal/edit_article.jsp
org.acme.liferay.journal.Util cannot be resolved to a type
227: String smallImageURL = BeanParamUtil.getString(article, request, "smallImageURL");
228: %>
229:
230: <h1><%= org.acme.liferay.journal.Util.getMeACoolH1(groupId) %></h1>
231:
232: <liferay-util:include page="/html/portlet/journal/article_tabs.jsp">
233: <liferay-util:param name="tabs1" value="content" />

Stacktrace:
at org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:92)
at org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:330)
at org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:439)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:349)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:327)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:314)
at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:592)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:317)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)
at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:551)
at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:488)
at com.liferay.taglib.util.IncludeTag.include(IncludeTag.java:175)
at com.liferay.taglib.util.IncludeTag._doInclude(IncludeTag.java:223)
at com.liferay.taglib.util.IncludeTag.doEndTag(IncludeTag.java:58)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)

Analyse

Was ist passiert? Offensichtlich kann unsere Klasse nicht aufgelöst werden. Öffnen wir das Web Archive unseres Hooks, stellen wir fest, dass alles an Ort und Stelle ist. Die JSP liegt, wo sie sein soll und auch die Klasse ist vorhanden. Also: Warum wird die Klasse nicht gefunden?

Um diese Frage beantworten zu können, muss man sich ein wenig mit dem Deployment-Verhalten von Liferay beschäftigt haben, denn es legt Sicherheitskopien von den originalen JSPs an und ersetzt diese durch die aus anderen Web Archiven.
Um das noch einmal zu verdeutlichen: Im eigenen Deployment-Verzeichnis von Liferay, also z.B. in <Tomcat>/webapps/ROOT, werden die Originale überschrieben.
Der Clou ist, dass dabei Klassen nicht mitkopiert werden, und da jedes Deployment einen eigenen ClassLoader hat, können angepasste JSPs nicht auf die Klassen zugreifen, die in dem Web Archive hinterlegt sind, aus dem sie stammen. Als logische Konsequenz gibt es Auslösungsfehler.

Was tun?

Mir fallen spontan zwei Lösungen ein:

  1. Die eigenen Klassen in das andere Deployment kopieren oder
  2. die eigenen Klassen als Bibliothek hinterlegen.

Für die begleitende Evaluierung dieses Blogbeitrags habe ich die erste Lösung gewählt, da sie schneller ist.

Auf ein Neues

Nachdem man die eigene Klasse in das Deployment von Liferay kopiert hat, funktioniert die kleine Anpassung und der Kunde ist überglücklich.

Was das Deployment-Verhalten von Liferay betrifft, bitte ich um Entschuldigung – ich muss jetzt fünf Minuten den Kopf schütteln.

Holisticon AG — Teile diesen Artikel

Über den Autor

Antwort hinterlassen