Download - Writing Plugged-in Java EE Apps: Jason Lee

Transcript
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()