In lieu of decent documentation, a ridiculous “Hello, World” example is presented below (to be taken as an example of what can, not what should be done).

Ridiculous Hello World Example

The following compiles, from String source, to 3 archives (API, Imp and App), complete with OSGi and JMPS meta. The archives are then run in various contexts (vanilla Java, JPMS Jigsaw and OSGi).

Create interdependent JARS

Create the API, Imp and App JARs in-memory from String sources.

API JAR

A pure API JAR, complete with OSGi and JPMS metadata, then signed (just for demonstration purposes):

	String moduleNameApi = "com.acme.api";	
	String versionApi = "1.0.0";	
	
	apiJar = compiling()
		.versionAt(latestSupported())
		.source(
			from(
				"package com.acme.api;                           \n" +

				"public interface Greet {                        \n" +

				"   public abstract String greeting(String to);  \n" +

				"}                                               \n")
		)
		.compile(
			toArchive()
		)
		.configured(
			asOsgiBundle()
				.named(moduleNameApi)
				.exporting(s -> true, attribute("version", versionApi))
		)
		.configured(
			asJpmsModule()
				.named(moduleNameApi)
				.exporting(s -> true)
				.autoRequiring()
		)
		.configured(
			withSignature()
				.digestedBy(SHA512)
				.store(keyStore)
				.alias(alias)
				.password(password)
				.signatureAlgorithm(SHA256_WITH_RSA)
				.createdBy(author)
		)
		.toObjectModel();
Imp JAR

An implementation JAR, depending on the in-memory API JAR previously created:

	String moduleNameImp = "com.acme.imp";	
	
	Archive impJar = compiling()
		.versionAt(latestSupported())
		.source(from(
			"package com.acme.imp;                              \n" +

			"import com.acme.api.Greet;                         \n" +

			"public class HelloService implements Greet {       \n" +

			"   @Override                                       \n" +
			"   public String greeting(String to)               \n" +
			"   {                                               \n" +
			"      return \"Hello \" + to;                      \n" +
			"   }                                               \n" +
			"}                                                  \n")
		)
		.source(from(
			"package com.acme.imp;                                     \n" +

			"import com.acme.api.Greet;                                \n" +
			"import org.osgi.framework.BundleActivator;                \n" + 
			"import org.osgi.framework.BundleContext;                  \n" + 
			"import org.osgi.framework.ServiceRegistration;            \n" + 

			"public class Activator implements BundleActivator {       \n" + 

			"   private ServiceRegistration<Greet> registration;       \n" + 

			"   @Override                                              \n" + 
			"   public void start(BundleContext ctx)                   \n" + 
			"   {                                                      \n" + 
			"      HelloService hello = new HelloService();            \n" + 
			"      registration =                                      \n" +
			"         ctx.registerService(Greet.class, hello, null);   \n" + 
			"   }                                                      \n" + 

			"   @Override                                              \n" + 
			"   public void stop(BundleContext ctx)                    \n" + 
			"   {                                                      \n" + 
			"      if(registration != null) {                          \n" + 
			"         registration.unregister();                       \n" + 
			"      }                                                   \n" + 
			"   }                                                      \n" + 
			"}                                                         \n")
		)
		.withDependencies(fromArchive(apiJar))
		.compile(
			toArchive()
		)
		.configured(
			asOsgiBundle()
				.named(moduleNameImp)
				.autoImporting()
				.autoImporting(byMappingBundleArchives(apiJar))
				.withActivator(moduleNameImp + ".Activator")
		)
		.configured(
			asJpmsModule()
				.named(moduleNameImp)
				.autoRequiring()
				.autoRequiring(fromArchives(apiJar))
				.providing(moduleNameApi + ".Greet", 
						singleton(moduleNameImp + ".HelloService")))
		.toObjectModel();

(Programmatic OSGi API above, swapped below, for declaratively annotated component)

App JAR

An Application JAR, executable with vanilla Java SPI, OSGi or JPMS:

	String moduleNameApp = "com.acme.app";	
	
	appJar = compiling()
		.versionAt(latestSupported())
		.source(from(
			"package com.acme.app;                                    \n" +

			"import com.acme.api.Greet;                               \n" +

			"import java.lang.Module;                                 \n" +
			"import java.lang.ModuleLayer;                            \n" +
			
			"import java.util.ServiceLoader;                          \n" + 
			"import java.util.stream.StreamSupport;                   \n" + 

			"import org.osgi.service.component.annotations.Activate;  \n" +
			"import org.osgi.service.component.annotations.Component; \n" +
			"import org.osgi.service.component.annotations.Reference; \n" +
			
			"@Component(immediate = true)                             \n" +
			"public class App {                                       \n" +
			
			"   @Reference                                            \n" +
			"   Greet greet;                                          \n" +
			
			"   @Activate                                             \n" +
			"   void activate()                                       \n" +
			"   {                                                     \n" +
			"      String greeting = greet.greeting(\"Modularity\");  \n" + 
			"      System.out.println(\"OSGi: \" + greeting);         \n" + 
			"   }                                                     \n" + 
			
			"   public static void main(String[] args)                \n" + 
			"   {                                                     \n" + 
			"      Class<Greet> srv = Greet.class;                    \n" +
			
			"      Module module = App.class.getModule();             \n" +
			"      ModuleLayer layer = module.getLayer();             \n" +
		
			"      ServiceLoader<Greet> services = (layer == null) ?  \n" +
			"               ServiceLoader.load(srv)                   \n" +
			"               :                                         \n" +
			"               ServiceLoader.load(layer, srv);           \n" +
			
			"      Greet implementation = StreamSupport               \n" +
			"               .stream(services.spliterator(), false)    \n" + 
			"               .findFirst()                              \n" + 
			"               .orElseThrow();                           \n" + 

			"      String msg = implementation.greeting(args[0]);     \n" + 
			"      System.out.println(module + \": \" + msg);         \n" + 
			"   }                                                     \n" + 
			"}                                                        \n")
		)
		.withDependencies(fromArchive(apiJar))
		.loggingTo(new PrintWriter(System.err))
		.consumingDiagnositicMessages(System.err::println)
		.compile(
			toArchive()
		)
		.configured(
			asJpmsModule()
				.named(moduleNameApp)
				.exporting(s -> s.startsWith("com.acme"))   // launch main
				.autoRequiring()
				.autoRequiring(fromArchives(apiJar, impJar))
				.launching("com.acme.app.App")
				.using("com.acme.api.Greet")
		)
		.configured(
			asOsgiBundle()
				.named(moduleNameApp)
				.autoImporting()
				.autoImporting(byMappingBundleArchives(apiJar, impJar))
				.withManifestHeader("Service-Component", "/OSGI-INF/scr.xml")
		)
		.with("/OSGI-INF/scr.xml", bytes(
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>               \n" + 
			"<scr:component name=\"example.app\"                      \n" + 
			"    xmlns:scr=\"http://www.osgi.org/xmlns/scr/v1.4.0\">  \n" +
			
			"    <implementation class=\"com.acme.app.App\" />        \n" +
			"    <reference                                           \n" +
			"    name=\"greet\"                                       \n" +
			"    interface=\"com.acme.api.Greet\"                     \n" +
			"    field=\"greet\" />                                   \n" +
			"</scr:component>")
		)
		.toObjectModel();

Load in-memory

Create a ClassLoader for subsequent runs:

	InMemoryClassLoader loader = ClassLoaders.inMemoryClassLoader()
			.jar(apiJar.toByteArray())
			.jar(impJar.toByteArray())
			.jar(appJar.toByteArray());

	Handler.addProtocolHandlerSystemProperty();
Run Vanilla

Run as vanilla Java (i.e. SPI not JPMS):

	Class<?> main = loader.loadClass("com.acme.app.App");
	Method method = Methods.getMethod(main, "main", String[].class).orElseThrow();

	Runnable run = Exceptional.uncheckRunnable(
			() -> method.invoke(null, new Object[] {new String[] {"Vanilla"}}));
	
	ClassLoaders.runInContext(loader, run);

The output is something like:

	unnamed module @3a60c416: Hello Vanilla
Run JPMS

An in-memory ModuleFinder is required (provided by lade.jpms. Execution is then:

	ModuleFinder inMemoryFinder = new InMemoryModuleFinder(loader);
	ModuleLayer parent = ModuleLayer.boot();
	
	Configuration cf = parent.configuration().resolveAndBind(
			ModuleFinder.of(), 
			inMemoryFinder, 
			Set.of(moduleNameApp));
	
	ModuleLayer layer = parent.defineModulesWithOneLoader(cf, loader);

	Class<?> mainClass = layer.findLoader(moduleNameApp).loadClass("com.acme.app.App");

	Method mainMethod = Methods.getMethod(mainClass, "main", String[].class).orElseThrow();
	mainMethod.invoke(null, new Object[] {new String[] {"In-memory"}});

The output is:

	module com.acme.app: Hello In-Memory
Run OSGi

Using felix.connect (aka PojoSR):

	List<BundleDescriptor> bundles = new ClasspathScanner().scanForBundles(loader);
	Map<String, Object> config = Collections.singletonMap(BUNDLE_DESCRIPTORS, bundles);

	PojoServiceRegistry registry = ServiceLoader.load(PojoServiceRegistryFactory.class).iterator()
			.next()
			.newPojoServiceRegistry(config);

The output is:

	OSGi: Hello Modularity

Onto boring filesystem

	Path apiPath = apiJar.to(Paths.get(".", "target", "example", "api.jar"));
	Path impPath = impJar.to(Paths.get(".", "target", "example", "imp.jar"));
	Path appPath = appJar.to(Paths.get(".", "target", "example", "app.jar"));
Run Filesystem JPMS
	char colon = pathSeparatorChar;
	String home = System.getProperty("java.home");
	String javaBin = home + separatorChar + "bin" + separatorChar + "java";

	ProcessBuilder processBuilder = new ProcessBuilder(javaBin,
			"--module-path", 
			apiPath.toString() + colon + impPath + colon + appPath,
			"--module", 
			moduleNameApp + '/' + "com.acme.app.App",
			"FileSystem");
	
	Process process = processBuilder.start();
	String output = 
			new String(IoStreams.readAllBytes(process.getInputStream()), UTF_8) +
			new String(IoStreams.readAllBytes(process.getErrorStream()), UTF_8);
	System.out.print(output);

The output is:

	module com.acme.app: Hello FileSystem

Back to top

Version: 0.1.0. Last Published: 2018-10-08.

Earcam Maven Skin.