/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.module.embedded.internal; import static com.google.common.base.Preconditions.checkState; import static java.util.stream.Collectors.toList; import static org.codehaus.plexus.util.FileUtils.fileWrite; import static org.codehaus.plexus.util.FileUtils.toFile; import static org.mule.maven.client.api.model.BundleScope.PROVIDED; import static org.mule.maven.client.api.MavenClientProvider.discoverProvider; import static org.mule.runtime.module.embedded.internal.Serializer.serialize; import org.mule.maven.client.api.model.BundleDependency; import org.mule.maven.client.api.model.BundleDescriptor; import org.mule.maven.client.api.MavenClient; import org.mule.maven.client.api.MavenClientProvider; import org.mule.maven.client.api.model.MavenConfiguration; import org.mule.runtime.module.embedded.api.ApplicationConfiguration; import org.mule.runtime.module.embedded.api.ContainerInfo; import org.mule.runtime.module.embedded.api.EmbeddedContainer; import org.mule.runtime.module.embedded.internal.classloading.JdkOnlyClassLoader; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; public class DefaultEmbeddedContainerBuilder implements EmbeddedContainer.EmbeddedContainerBuilder { private String muleVersion; private URL containerBaseFolder; private ApplicationConfiguration applicationConfigruation; private String log4jConfigurationFile; private MavenConfiguration mavenConfiguration; @Override public EmbeddedContainer.EmbeddedContainerBuilder withMuleVersion(String muleVersion) { this.muleVersion = muleVersion; return this; } @Override public EmbeddedContainer.EmbeddedContainerBuilder withContainerBaseFolder(URL containerBaseFolder) { this.containerBaseFolder = containerBaseFolder; return this; } @Override public EmbeddedContainer.EmbeddedContainerBuilder withApplicationConfiguration(ApplicationConfiguration applicationConfigruation) { this.applicationConfigruation = applicationConfigruation; return this; } @Override public EmbeddedContainer.EmbeddedContainerBuilder withLog4jConfigurationFile(String log4JConfigurationFile) { this.log4jConfigurationFile = log4JConfigurationFile; return this; } @Override public EmbeddedContainer.EmbeddedContainerBuilder withMavenConfiguration(MavenConfiguration mavenConfiguration) { this.mavenConfiguration = mavenConfiguration; return this; } @Override public EmbeddedContainer build() { checkState(muleVersion != null, "muleVersion cannot be null"); checkState(containerBaseFolder != null, "containerBaseFolder cannot be null"); checkState(applicationConfigruation != null, "application cannot be null"); checkState(mavenConfiguration != null, "mavenConfiguration cannot be null"); try { JdkOnlyClassLoader jdkOnlyClassLoader = new JdkOnlyClassLoader(); if (log4jConfigurationFile != null) { configureLogging(jdkOnlyClassLoader); } MavenClientProvider mavenClientProvider = discoverProvider(getClass().getClassLoader()); MavenClient mavenClient = mavenClientProvider.createMavenClient(mavenConfiguration); MavenContainerClassLoaderFactory classLoaderFactory = new MavenContainerClassLoaderFactory(mavenClient); ClassLoader containerModulesClassLoader = classLoaderFactory.create(muleVersion, jdkOnlyClassLoader, containerBaseFolder); List<URL> services = classLoaderFactory.getServices(muleVersion); ContainerInfo containerInfo = new ContainerInfo(muleVersion, containerBaseFolder, services); if (mavenConfiguration != null) { persistMavenConfiguration(containerBaseFolder, mavenConfiguration); } ClassLoader embeddedControllerBootstrapClassLoader = createEmbeddedImplClassLoader(containerModulesClassLoader, mavenClient, muleVersion); try { Class<?> controllerClass = embeddedControllerBootstrapClassLoader.loadClass("org.mule.runtime.module.embedded.impl.EmbeddedController"); Constructor<?> constructor = controllerClass.getConstructor(byte[].class, byte[].class); ByteArrayOutputStream containerOutputStream = new ByteArrayOutputStream(512); serialize(containerInfo, containerOutputStream); ByteArrayOutputStream appConfigOutputStream = new ByteArrayOutputStream(512); serialize(applicationConfigruation, appConfigOutputStream); Object o = constructor.newInstance(containerOutputStream.toByteArray(), appConfigOutputStream.toByteArray()); return new EmbeddedContainer() { @Override public void start() { try { Method startMethod = o.getClass().getMethod("start"); startMethod.invoke(o); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new IllegalStateException(cause); } } catch (Exception e) { throw new IllegalStateException(e); } } @Override public void stop() { try { Method stopMethod = o.getClass().getMethod("stop"); stopMethod.invoke(o); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new IllegalStateException(cause); } } catch (Exception e) { throw new IllegalStateException(e); } } }; } catch (Exception e) { throw new IllegalStateException("Cannot create embedded container", e); } } catch (Exception e) { throw new RuntimeException(e); } } private void configureLogging(JdkOnlyClassLoader jdkOnlyClassLoader) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { final Class<?> log4jLogManagerClass = jdkOnlyClassLoader.loadClass("org.apache.logging.log4j.LogManager"); final Object logContext = log4jLogManagerClass.getMethod("getContext", boolean.class).invoke(null, false); final Class<?> log4jLoggerContextClass = jdkOnlyClassLoader.loadClass("org.apache.logging.log4j.core.LoggerContext"); log4jLoggerContextClass.getMethod("setConfigLocation", URI.class).invoke(logContext, new File(log4jConfigurationFile).toURI()); } private void persistMavenConfiguration(URL containerBaseFolder, MavenConfiguration mavenConfiguration) throws IOException { File configurationFolder = new File(toFile(containerBaseFolder), "conf"); if (!configurationFolder.exists()) { if (!configurationFolder.mkdirs()) { throw new IllegalArgumentException("Could not create MULE_HOME/conf folder in: " + configurationFolder.getAbsolutePath()); } } JsonObject rootObject = new JsonObject(); JsonObject muleRuntimeConfigObject = new JsonObject(); rootObject.add("muleRuntimeConfig", muleRuntimeConfigObject); JsonObject mavenObject = new JsonObject(); muleRuntimeConfigObject.add("maven", mavenObject); if (!mavenConfiguration.getMavenRemoteRepositories().isEmpty()) { JsonObject repositoriesObject = new JsonObject(); mavenObject.add("repositories", repositoriesObject); mavenConfiguration.getMavenRemoteRepositories().forEach(mavenRepo -> { JsonObject repoObject = new JsonObject(); repositoriesObject.add(mavenRepo.getId(), repoObject); repoObject.addProperty("url", mavenRepo.getUrl().toString()); mavenRepo.getAuthentication().ifPresent(authentication -> { repoObject.addProperty("username", authentication.getUsername()); repoObject.addProperty("password", authentication.getPassword()); }); }); } mavenObject.addProperty("repositoryLocation", mavenConfiguration.getLocalMavenRepositoryLocation().getAbsolutePath()); String muleConfigContent = new Gson().toJson(rootObject); fileWrite(new File(configurationFolder, "mule-config.json"), muleConfigContent); } private static ClassLoader createEmbeddedImplClassLoader(ClassLoader parentClassLoader, MavenClient mavenClient, String muleVersion) throws MalformedURLException { BundleDescriptor embeddedControllerImplDescriptor = new BundleDescriptor.Builder().setGroupId("org.mule.distributions") .setArtifactId("mule-module-embedded-impl").setVersion(muleVersion).setType("jar").build(); BundleDependency embeddedBundleImplDependency = mavenClient.resolveBundleDescriptor(embeddedControllerImplDescriptor); List<BundleDependency> embeddedImplDependencies = mavenClient.resolveBundleDescriptorDependencies(false, embeddedControllerImplDescriptor); List<URL> embeddedUrls = embeddedImplDependencies.stream() .filter(bundleDependency -> !bundleDependency.getScope().equals(PROVIDED)) .map(BundleDependency::getBundleUrl) .collect(toList()); embeddedUrls = new ArrayList<>(embeddedUrls); embeddedUrls.add(embeddedBundleImplDependency.getBundleUrl()); URLClassLoader urlClassLoader = new URLClassLoader(embeddedUrls.toArray(new URL[embeddedUrls.size()]), parentClassLoader); return urlClassLoader; } }