Templates and Structures make Web Content. Web Content is simply text until it’s interpreted by the consumer, whether that’s a browser, elements with a src or html tag, JSP, or portal_normal.vm. This example builds on the previous Liferay Themes examples in this blog1. Read those first.
Let’s bend a spoon.
The structure and template included here are not complicated. Their application isn’t complicated. The structure presents the user with a single select box. The template outputs the chosen value as part of a larger literal string. It’s all just text.
But our consumer is a JSP in the theme context. The JSP references classes included in a custom .jar (wrightui.jar), which has been included in our (otherwise vanilla) Tomcat’s classpath (C:liferay-portal-5.2.3tomcat-6.0.18libext). The JSP consumes our web content string, dynamically compiles it into an executable Java class at runtime, and executes a method on it.
Though this example uses a JSP in the theme to demonstrate this mechanism, it can be used in portlets, and even to affect the portal behavior directly. The value of this technique isn’t really in the string of output, but in the idea that we can leverage structures to rapidly build user interfaces, which then provide easy access and real-time management of application rules.
wrightui.jar
First, we have to build our .jar file. This isn’t a Java tutorial, but you’ll need the classes below. In addition to these classes, you’ll need to include tools.jar, which is a Java resource available with the JDK. Put the 7 files below in an eclipse project, build a jar file, and put that jar (wrightui.jar) along with tools.jar into your portal at $catalina_home/lib/ext.
package com.wrightui.jit; import java.util.*; public class ByteArrayClassLoader extends ClassLoader { private Map<String, ByteArrayJavaFileObject> cache = new HashMap<String, ByteArrayJavaFileObject>(); public ByteArrayClassLoader() throws Exception { super(ByteArrayClassLoader.class.getClassLoader()); } public void put(String name, ByteArrayJavaFileObject obj) { ByteArrayJavaFileObject co = cache.get(name); if (co == null) { cache.put(name, obj); } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> cls = null; try { ByteArrayJavaFileObject co = cache.get(name); if (co != null) { byte[] ba = co.getClassBytes(); cls = defineClass(name, ba, 0, ba.length); } } catch (Exception ex) { throw new ClassNotFoundException("Class name: " + name, ex); } return cls; } }ByteArrayClassLoader.java
package com.wrightui.jit; import java.io.*; import java.net.*; import javax.tools.*; public class ByteArrayJavaFileObject extends SimpleJavaFileObject { private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); public ByteArrayJavaFileObject(String name, Kind kind) { super(URI.create(name.replace('.', '/') + kind.extension), kind); } public byte[] getClassBytes() { return bos.toByteArray(); } @Override public OutputStream openOutputStream() throws IOException { return bos; } }ByteArrayJavaFileObject.java
package com.wrightui.jit; public class DynamicAPI { public static int[] nums; public static DynamicClass func; public static String sourceCode = "package com.covisint.jit;"+ "public class JSPDynamic implements DynamicClass"+ " { public String renderMethod() { return "Works"; } "+ "}"; public static void main(String[] args) { compile(); } public static void compile() { compile(DynamicAPI.sourceCode); } public static void compile(String sourceCode) { try { DynamicCompiler compiler = new DynamicCompiler(); compiler.init(); Class<?> clazz = null; clazz = compiler.compileToClass("com.covisint.jit.JSPDynamic", sourceCode); func = (DynamicClass) clazz.newInstance(); } catch (Throwable ex) { } execute(); } /* CLASS EXECUTION */ public static void execute() { try { System.out.println(func.renderMethod()); } catch (Throwable ex) { } } }DynamicAPI.java
package com.wrightui.jit; public interface DynamicClass { public abstract String renderMethod(); }DynamicClass.java
package com.wrightui.jit; import java.io.*; import javax.tools.*; import javax.tools.JavaFileObject.Kind; public class DynamicClassFileManager <FileManager> extends ForwardingJavaFileManager<JavaFileManager> { private ByteArrayClassLoader loader = null; DynamicClassFileManager(StandardJavaFileManager mgr) { super(mgr); try { loader = new ByteArrayClassLoader(); } catch (Exception ex) { ex.printStackTrace(System.out); } } @Override public JavaFileObject getJavaFileForOutput(Location location, String name, Kind kind, FileObject sibling) throws IOException { ByteArrayJavaFileObject co = new ByteArrayJavaFileObject(name, kind); loader.put(name, co); return co; } @Override public ClassLoader getClassLoader(Location location) { return loader; } }DynamicClassFileManager.java
package com.wrightui.jit; import java.util.*; import javax.tools.*; import javax.tools.JavaCompiler.CompilationTask; public class DynamicCompiler { static String portalClassPath = "/C:set_your_classpath_before_deploying"; //static String[] opts = new String[] { "-classpath", portalClassPath }; static String[] opts = new String[] { }; private JavaCompiler compiler; private DiagnosticCollector<JavaFileObject> collector; private JavaFileManager manager; public void init() throws Exception { compiler = ToolProvider.getSystemJavaCompiler(); collector = new DiagnosticCollector<JavaFileObject>(); manager = new DynamicClassFileManager<JavaFileManager>(compiler.getStandardFileManager(null, null, null)); } public Class<?> compileToClass(String fullName, String javaCode) throws Exception { Class<?> clazz = null; StringJavaFileObject strFile = new StringJavaFileObject(fullName, javaCode); Iterable<? extends JavaFileObject> units = Arrays.asList(strFile); CompilationTask task = compiler.getTask(null, manager, collector, Arrays.asList(opts), null, units); boolean status = task.call(); if (status) { System.out.printf("Compilation successful!!!n"); clazz = manager.getClassLoader(null).loadClass(fullName); } else { System.out.printf("Message:n"); for (Diagnostic<?> d : collector.getDiagnostics()) { System.out.printf("%sn", d.getMessage(null)); } System.out.printf("***** Compilation failed!!!n"); } return clazz; } static String getDemoSource() { StringBuilder sb = new StringBuilder(); //TEST Class File Definition sb.append("package com.wrightui.jit; "); sb.append("public class JSPDynamic implements DynamicClass { "); sb.append(" public String renderMethod() { "); sb.append(" return "Compilation Successful from JSPDynamic"; "); sb.append(" } "); sb.append("} "); return sb.toString(); } public static void main(String[] args) { DynamicClass func; String sourceCode = DynamicCompiler.getDemoSource(); try { DynamicCompiler compiler = new DynamicCompiler(); compiler.init(); Class<?> clazz = null; clazz = compiler.compileToClass("com.wrightui.jit.JSPDynamic", sourceCode); func = (DynamicClass) clazz.newInstance(); System.out.println(func.renderMethod()); } catch (Exception E) { System.out.println("No Compilation: "); E.printStackTrace(); } } }DynamicCompiler.java
package com.wrightui.jit; import java.net.*; import javax.tools.*; public class StringJavaFileObject extends SimpleJavaFileObject { private String source; public StringJavaFileObject(String name, String source) { super(URI.create(name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); this.source = source; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return this.source; } }StringJavaFileObject.java
JSP
You’re going to need a JSP file to launch this from. If you’re building a theme using the Liferay SDK (recommended by Liferay), create the following file in your theme project: _diffs/jsp/dynamic.jsp
<%@page import="com.wrightui.jit.*"%> <% String articleSourceCode = (String)request.getAttribute("sourceCode"); int[] nums; DynamicClass func; String sourceCode = articleSourceCode; try { DynamicCompiler compiler = new DynamicCompiler(); compiler.init(); Class<?> clazz = null; clazz = compiler.compileToClass("com.wrightui.jit.JSPDynamic", sourceCode); func = (DynamicClass) clazz.newInstance(); out.println(func.renderMethod()); } catch (Exception E) { out.println("No Compilation"); } %>dynamic.jsp
portal_normal.vm
The following is an excerpt from our theme’s _diffs/templates/portal_normal.vm file.
#parse ($init) <html dir="#language ("lang.dir")" xmlns="http://www.w3.org/1999/xhtml"> <head> <title> #set ($a2 = "your article id"); #set ($a1 = "your group id"); #set ($dynamic_content = $journalContentUtil.getContent($a1, $a2, "", "view", "en", $themeDisplay)) $request.setAttribute("sourceCode", $dynamic_content) $theme.include($themeServletContext, "/jsp/dynamic_code.jsp") </title> $theme.include($top_head_include) </head> <body> ## REST OF PORTAL_NORMAL.vmportal_normal.vm
Structure and Template
Finally, you’ll need a structure and template to get and process information from the user.
<root> <dynamic-element name='select' type='list' repeatable='false'> <dynamic-element name='dynamically_compiled1' type='dynamically_compiled1' repeatable='false'> </dynamic-element> <dynamic-element name='dynamically_compiled2' type='dynamically_compiled2' repeatable='false'> </dynamic-element> </dynamic-element> </root>(structure)
package com.wrightui.jit; public class JSPDynamic implements DynamicClass { public String renderMethod() { return "$select.getData()"; } }(template)
RUN THE EXAMPLE
1. Create wrightui.jar using the 7 class files above (you’ll need to set your classpath)
2. Add wrightui.jar and tools.jar to your $catalina_home/lib/ext folder and start your portal.
3. Add your template and structure.
4. Create a web content item using the template you created.
5. Add dynamic.jsp to your _diffs/jsp folder
6. Edit portal_normal.vm to retrieve your web content (now java source code)
7. Edit portal_normal.vm to pass the java source to the jsp (via a request attribute)
8. Edit portal_normal.vm to include your jsp ouput.
Load your pages, and check the html document title!
Although we’re only creating an HTML title here, the java classes you’re compiling could actually be used to implement interfaces that can provide business functionality. This means you can allow non-programmers to edit certain types of business rules in their applications without exposing pure java, at run time, and without unnecessary deployments.

