Writing Plugged-in Java EE Apps: Jason Lee

18
<Insert Picture Here> Writing Plugged-In Java EE Apps Jason Lee July 9, 2012

description

From continuous integration servers to blogging systems, we&#x27;ve all seen and used pluggable applications. Writing our own though can be an elusive task. That need not be the case, though, as the Java EE spec contains all you need to do just that. In this session, we&#x27;ll see how we can leverage the power of CDI to write, for example, easily extensible JSF applications. When the session is over, you&#x27;ll have all you need to write the next killer app, and, thanks to Java EE, you&#x27;ll be surprised to see how little work it really is.

Transcript of Writing Plugged-in Java EE Apps: Jason Lee

Page 1: Writing Plugged-in Java EE Apps: Jason Lee

<Insert Picture Here>

Writing Plugged-In Java EE Apps

Jason Lee

July 9, 2012

Page 2: Writing Plugged-in Java EE Apps: Jason Lee

2Oracle Confidential

Who Am I?

• Principal MTS for Oracle Corporation (by way of Sun Microsystems)

• Member of GlassFish team– RESTful Management APIs– Administration Console

• JSF user/developer• Live in Oklahoma City, OK

Page 3: Writing Plugged-in Java EE Apps: Jason Lee

3Oracle Confidential

Introduction

• Plugins are not a new concept– Means of providing extensibility or decomposing and decoupling

functionality– The concept has been around for years

• NetBeans/Eclipse/IDEA• Wordpress• Hudson

• The what and why are easy. The how is the hard part.– How do I get access to the plugin code?– How do I identify the plugins deployed with the system– How do I expose core system functionality to the plugins

Page 4: Writing Plugged-in Java EE Apps: Jason Lee

4Oracle Confidential

Introduction – cont.

• Some background– GlassFish 3.x Administration Console

• Core application does very little• All actual functionality delivered as plugins• Based on proprietary technologies

– HK2/OSGi– JSFTemplating

– For GlassFish 4.x, the basic stack had to change• Reevaluate whole system• Investigate HK2/OSGi, CDI, existing systems, etc.• Design a system based on modern technologies, preferably

standards

Page 5: Writing Plugged-in Java EE Apps: Jason Lee

5Oracle Confidential

Problem #1 – Class Loading

• The hard part– Where do I put the jar files?– How do I load them?

• Three (probably of many) choices– Repackaging– Manual ClassLoading– OSGi

Page 6: Writing Plugged-in Java EE Apps: Jason Lee

6Oracle Confidential

Problem #1 - Class Loading - Repackaging

• Simple and portable– Guaranteed to support every Java EE technology supported

in .war files

• Ant-/Maven-/etc-based– Base distribution war– Collection of plugins– War rebuilt to include plugin jars

• Liferay uses this. Works well.• Redeploys/upgrades take a bit more time/work

Page 7: Writing Plugged-in Java EE Apps: Jason Lee

7Oracle Confidential

#!/bin/bash

DIST=$1

if [ "$DIST" == "" ] ; then

echo "You must specify the distribution .war"

exit 1

fi

BASE=`echo $DIST | sed -e 's/\.war//'`

rm -rf work

mkdir work

cd work

jar xf ../$DIST

cp ../plugins/*jar WEB-INF/lib

jar cf ../$BASE-repackaged.war *

cd ..

rm -rf work

Problem #1 - Class Loading - RepackagingProblem #1 - Class Loading - Repackaging

Page 8: Writing Plugged-in Java EE Apps: Jason Lee

8Oracle Confidential

Problem #1 - Class Loading – Manual ClassLoading

• Filesystem-based– Much like Wordpress

• wordpress/wp-content/plugins/*– ~/.plugins

• War is deployed unchanged• Fairly portable, in theory

– Demo works on GlassFish– Currently breaks on JBoss– Should be easily solvable

• Demo - Plummer

Page 9: Writing Plugged-in Java EE Apps: Jason Lee

9Oracle Confidential

public class PluginLoader implements Extension {

// ...

public void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd,

BeanManager beanManager) {

for (PluginFinder pluginFinder : getPluginFinders()) {

try {

for (Class<?> clazz : pluginFinder.getClasses()) {

final AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(clazz);

logger.log(Level.INFO, "Adding AnnotatedType for {0}", annotatedType.toString());

bbd.addAnnotatedType(annotatedType);

}

} catch (Exception ex) {

Logger.getLogger(PluginLoader.class.getName()).log(Level.SEVERE, null, ex);

}

}

}

// ...

}

Problem #1 - Class Loading – Manual ClassLoading

Page 10: Writing Plugged-in Java EE Apps: Jason Lee

10Oracle Confidential

Problem #1 – Class Loading - OSGi

• Well-defined and understood solution

• Web Application Bundles not quite what we need– Requires repackaging

• Container must support OSGi• Deployment will likely vary between containers

Page 11: Writing Plugged-in Java EE Apps: Jason Lee

11Oracle Confidential

public class PluginActivator implements BundleActivator {

@Override

public void start(BundleContext context) throws Exception {

ServiceTracker tracker = new ServiceTracker(context, PluginTracker.class.getName(), null);

tracker.open();

PluginTracker pt = (PluginTracker)tracker.getService();

if (pt != null) {

pt.registerPluginBundle(context.getBundle());

}

tracker.close();

}

@Override

public void stop(BundleContext context) throws Exception {

}

}

Problem #1 – Class Loading - OSGi

Page 12: Writing Plugged-in Java EE Apps: Jason Lee

12Oracle Confidential

public class PluginTrackerImpl implements PluginTracker {

@Override

public void registerPluginBundle(Bundle bundle) {

if (bundle.getEntry("META-INF/beans.xml") != null) {

Enumeration<URL> e = bundle.findEntries("/", "*.class", true);

while (e.hasMoreElements()) {

String className = e.nextElement().getPath().substring(1).replace("/", ".");

className = className.substring(0, className.length()-6);

classes.add(className);

}

}

}

public Set<String> getClasses() {

return classes;

}

}

Problem #1 – Class Loading - OSGi

Page 13: Writing Plugged-in Java EE Apps: Jason Lee

13Oracle Confidential

public class PlummerActivator implements BundleActivator {

@Override

public void start(BundleContext context) throws Exception {

context.registerService(PluginTracker.class.getName(), PluginTrackerImpl.instance(),

new Properties());

}

@Override

public void stop(BundleContext context) throws Exception {

context.ungetService(context.getServiceReference(PluginTracker.class.getName()));

}

}

Problem #1 – Class Loading - OSGi

Page 14: Writing Plugged-in Java EE Apps: Jason Lee

14Oracle Confidential

Problem #2 – Application Design

• Largely application-specific

• General strategies and techniques can be defined

• Once plugins are loaded, how are they integrated into the application?– Java EE to the rescue

• JSF• CDI• REST

Page 15: Writing Plugged-in Java EE Apps: Jason Lee

15Oracle Confidential

JSF Extensibility

• Views decomposed into fragments• Custom component used to insert fragments

– pl:viewFragment

• Current solution is Mojarra-specific– MyFaces solution needed– Can also be implemented in Swing/JavaFX

Page 16: Writing Plugged-in Java EE Apps: Jason Lee

16Oracle Confidential

CDI – The Real Work Horse

• CDI Events– Pub/Sub– Loose coupling– Multiple Receivers

• Programmatic Bean Lookup– Instance<Foo>– Iterate over over instances

Page 17: Writing Plugged-in Java EE Apps: Jason Lee

17Oracle Confidential

JAX-RS Resources

• Plugins can provide REST resources

• Configure using Application rather than a package– javax.ws.rs.core.Application– <servlet>

<servlet-name>Jersey Web Application</servlet-name>

<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer

</servlet-class>

<init-param>

<param-name>javax.ws.rs.Application</param-name>

<param-value> org.glassfish.plummer.kernel.rest.RestApplication </param-value>

</init-param>

</servlet>

Page 18: Writing Plugged-in Java EE Apps: Jason Lee

18Oracle Confidential

JAX-RS Resources – Part 2

• Use CDI to find JAX-RS resources– Use a marker interface, e.g. RestResource– Look up BeanManager in JNDI

• BeanManager beanManager = (BeanManager) initialContext.lookup("java:comp/BeanManager");

– Ask CDI for the RestResource instances• beanManager.getBeans(RestResource.class);

– Return set of Classes• Application.getClasses()