Java tutorial
package org.ops4j.pax.construct.lifecycle; /* * Copyright 2007 Stuart McCulloch * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.File; import java.io.IOException; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.installer.ArtifactInstallationException; import org.apache.maven.artifact.installer.ArtifactInstaller; import org.apache.maven.artifact.manager.WagonManager; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectBuilder; import org.apache.maven.project.ProjectBuildingException; import org.apache.maven.project.artifact.InvalidDependencyVersionException; import org.apache.maven.settings.Mirror; import org.apache.maven.settings.Settings; import org.codehaus.plexus.util.IOUtil; import org.ops4j.pax.construct.util.PomUtils; import org.ops4j.pax.construct.util.StreamFactory; /** * Provision all local and imported bundles onto the selected OSGi framework * * <code><pre> * mvn pax:provision [-Dframework=felix|equinox|kf|concierge] [-Dprofiles=log,war,spring,...] * </pre></code> * * If you don't have Pax-Runner in your local Maven repository this command * will automatically attempt to download the latest release. It will then * continue to use this locally installed version of Pax-Runner unless you * add <code>-U</code> to force it to check online for a later release, or * <code>-Drunner=version</code> to temporarily use a different version. * * @goal provision * @aggregator true * * @requiresProject false * @requiresDependencyResolution test */ public class ProvisionMojo extends AbstractMojo { /** * Maven groupId for the new Pax-Runner */ private static final String PAX_RUNNER_GROUP = "org.ops4j.pax.runner"; /** * Maven artifactId for the new Pax-Runner */ private static final String PAX_RUNNER_ARTIFACT = "pax-runner"; /** * Main entry-point for the new Pax-Runner */ private static final String PAX_RUNNER_METHOD = "org.ops4j.pax.runner.Run"; /** * Accumulated set of bundles to be deployed */ private static List m_bundleIds; /** * Component for resolving Maven metadata * * @component */ private ArtifactMetadataSource m_source; /** * Component factory for Maven artifacts * * @component */ private ArtifactFactory m_factory; /** * Component for resolving Maven artifacts * * @component */ private ArtifactResolver m_resolver; /** * Component for installing Maven artifacts * * @component */ private ArtifactInstaller m_installer; /** * Component factory for Maven projects * * @component */ private MavenProjectBuilder m_projectBuilder; /** * The local Maven settings. * * @parameter expression="${settings}" * @required * @readonly */ private Settings m_settings; /** * List of remote Maven repositories for the containing project. * * @parameter expression="${project.remoteArtifactRepositories}" * @required * @readonly */ private List m_remoteRepos; /** * The local Maven repository for the containing project. * * @parameter expression="${localRepository}" * @required * @readonly */ private ArtifactRepository m_localRepo; /** * @parameter expression="${project}" * @required * @readonly */ private MavenProject m_project; /** * The current Maven reactor. * * @parameter expression="${reactorProjects}" * @required * @readonly */ private List m_reactorProjects; /** * Name of the OSGi framework to deploy onto. * * @parameter expression="${framework}" */ private String framework; /** * When true, start the OSGi framework and deploy the provisioned bundles. * * @parameter expression="${deploy}" default-value="true" */ private boolean deploy; /** * Comma separated list of additional Pax-Runner profiles to deploy. * * @parameter expression="${profiles}" */ private String profiles; /** * URL of file containing additional Pax-Runner arguments. * * @parameter expression="${args}" */ private String args; /** * Ignore bundle dependencies when deploying project. * * @parameter expression="${noDeps}" */ private boolean noDependencies; /** * Comma separated list of additional POMs with bundles as dependencies. * * @parameter expression="${deployPoms}" */ private String deployPoms; /** * Comma separated list of additional bundle URLs to deploy. * * @parameter expression="${deployURLs}" */ private String deployURLs; /** * The version of Pax-Runner to use for provisioning. * * @parameter expression="${runner}" default-value="RELEASE" */ private String runner; /** * A set of provision commands for Pax-Runner. * * @parameter expression="${provision}" */ private String[] provision; /** * Component factory for Maven repositories. * * @component */ private ArtifactRepositoryFactory m_repoFactory; /** * @component roleHint="default" */ private ArtifactRepositoryLayout m_defaultLayout; /** * Component for calculating mirror details. * * @component */ private WagonManager m_wagonManager; /** * Runtime helper available on Maven 2.0.9 and above. */ private Method m_getMirrorRepository; /** * {@inheritDoc} */ public void execute() throws MojoExecutionException { m_bundleIds = new ArrayList(); if (deployPoms != null) { addAdditionalPoms(); } if (m_project.getFile() != null) { for (Iterator i = m_reactorProjects.iterator(); i.hasNext();) { addProjectBundles((MavenProject) i.next(), false == noDependencies); } } setupRuntimeHelpers(); deployBundles(); } /** * Use reflection to find if certain runtime helper methods are available. */ private void setupRuntimeHelpers() { try { m_getMirrorRepository = m_wagonManager.getClass().getMethod("getMirrorRepository", new Class[] { ArtifactRepository.class }); } catch (RuntimeException e) { m_getMirrorRepository = null; } catch (NoSuchMethodException e) { m_getMirrorRepository = null; } } /** * Does this look like a provisioning POM? ie. artifactId of 'provision', packaging type 'pom', with dependencies * * @param project a Maven project * @return true if this looks like a provisioning POM, otherwise false */ public static boolean isProvisioningPom(MavenProject project) { // ignore POMs which don't have provision as their artifactId if (!"provision".equals(project.getArtifactId())) { return false; } // ignore POMs that produce actual artifacts if (!"pom".equals(project.getPackaging())) { return false; } // ignore POMs with no dependencies at all List dependencies = project.getDependencies(); if (dependencies == null || dependencies.size() == 0) { return false; } return true; } /** * Adds project artifact (if it's a bundle) to the deploy list as well as any non-optional bundle dependencies * * @param project a Maven project * @param checkDependencies when true, check project dependencies for other bundles to provision */ private void addProjectBundles(MavenProject project, boolean checkDependencies) { if (PomUtils.isBundleProject(project, m_resolver, m_remoteRepos, m_localRepo, true)) { provisionBundle(project.getArtifact()); } if (checkDependencies || isProvisioningPom(project)) { addProjectDependencies(project); } } /** * Adds any non-optional bundle dependencies to the deploy list * * @param project a Maven project */ private void addProjectDependencies(MavenProject project) { Set artifacts = project.getArtifacts(); for (Iterator i = artifacts.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); if (!artifact.isOptional() && !Artifact.SCOPE_TEST.equals(artifact.getScope())) { provisionBundle(artifact); } } } /** * @param bundle potential bundle artifact */ private void provisionBundle(Artifact bundle) { if ("pom".equals(bundle.getType())) { return; } // force download here, as next check tries to avoid downloading where possible if (!PomUtils.downloadFile(bundle, m_resolver, m_remoteRepos, m_localRepo)) { getLog().warn("Skipping missing artifact " + bundle); return; } if (PomUtils.isBundleArtifact(bundle, m_resolver, m_remoteRepos, m_localRepo, true)) { String version = PomUtils.getMetaVersion(bundle); String id = bundle.getGroupId() + ':' + bundle.getArtifactId() + ':' + version + ':' + bundle.getType(); if (!m_bundleIds.contains(id)) { m_bundleIds.add(id); } } else { getLog().warn("Skipping non-bundle artifact " + bundle); } } /** * Add user supplied POMs as if they were in the Maven reactor */ private void addAdditionalPoms() { String[] pomPaths = deployPoms.split(","); for (int i = 0; i < pomPaths.length; i++) { File pomFile = new File(pomPaths[i].trim()); if (pomFile.exists()) { try { addProjectBundles(m_projectBuilder.build(pomFile, m_localRepo, null), true); } catch (ProjectBuildingException e) { getLog().warn("Unable to build Maven project for " + pomFile); } } } } /** * Create deployment POM and pass it onto Pax-Runner for provisioning * * @throws MojoExecutionException */ private void deployBundles() throws MojoExecutionException { if (m_bundleIds.size() == 0) { getLog().info("~~~~~~~~~~~~~~~~~~~"); getLog().info(" No bundles found! "); getLog().info("~~~~~~~~~~~~~~~~~~~"); } List bundles = resolveProvisionedBundles(); MavenProject deployProject = createDeploymentProject(bundles); installDeploymentPom(deployProject); if (!deploy) { getLog().info("Skipping OSGi deployment"); return; } m_remoteRepos.add(getOps4jRepository()); // can remove this once runner is on central String delim = ""; StringBuffer repoListBuilder = new StringBuffer("+"); for (Iterator i = m_remoteRepos.iterator(); i.hasNext();) { repoListBuilder.append(delim); ArtifactRepository repo = (ArtifactRepository) i.next(); repoListBuilder.append(getRepositoryURL(repo)); if (repo.getSnapshots().isEnabled()) { repoListBuilder.append("@snapshots"); } if (false == repo.getReleases().isEnabled()) { repoListBuilder.append("@noreleases"); } delim = ","; } if (PomUtils.needReleaseVersion(runner)) { // find the latest release of Pax-Runner by querying the local and remote repos... Artifact runnerProject = m_factory.createProjectArtifact(PAX_RUNNER_GROUP, PAX_RUNNER_ARTIFACT, runner); runner = PomUtils.getReleaseVersion(runnerProject, m_source, m_remoteRepos, m_localRepo, null); } /* * Dynamically load the correct Pax-Runner code */ Pattern classicVersion = Pattern.compile("0\\.[1-4]\\.\\d"); if (classicVersion.matcher(runner).matches()) { Class clazz = loadRunnerClass("org.ops4j.pax", "runner", PAX_RUNNER_METHOD, false); deployRunnerClassic(clazz, deployProject, repoListBuilder.toString()); } else { Class clazz = loadRunnerClass(PAX_RUNNER_GROUP, PAX_RUNNER_ARTIFACT, PAX_RUNNER_METHOD, true); deployRunnerNG(clazz, deployProject, repoListBuilder.toString()); } } /** * @param repo remote Maven repository * @return repository (or mirror) URL */ private String getRepositoryURL(ArtifactRepository repo) { if (null != m_getMirrorRepository) { try { ArtifactRepository mirror = (ArtifactRepository) m_getMirrorRepository.invoke(m_wagonManager, new Object[] { repo }); if (null != mirror) { return mirror.getUrl(); } } catch (RuntimeException e) { getLog().warn(e); } catch (IllegalAccessException e) { getLog().warn(e); } catch (InvocationTargetException e) { getLog().warn(e); } } Mirror mirror = m_settings.getMirrorOf(repo.getId()); if (null != mirror) { return mirror.getUrl(); } mirror = m_settings.getMirrorOf("*"); if (null != mirror) { return mirror.getUrl(); } return repo.getUrl(); } /** * Attempt to resolve each provisioned bundle, and warn about any we can't find * * @return list of bundles to be deployed (as Maven dependencies) */ private List resolveProvisionedBundles() { List dependencies = new ArrayList(); for (Iterator i = m_bundleIds.iterator(); i.hasNext();) { String id = (String) i.next(); String[] fields = id.split(":"); Dependency dep = new Dependency(); dep.setGroupId(fields[0]); dep.setArtifactId(fields[1]); dep.setVersion(fields[2]); dep.setType(fields[3]); dep.setScope(Artifact.SCOPE_PROVIDED); dependencies.add(dep); } return dependencies; } /** * Create new POM (based on the root POM) which lists the deployed bundles as dependencies * * @param bundles list of bundles to be deployed * @return deployment project * @throws MojoExecutionException */ private MavenProject createDeploymentProject(List bundles) throws MojoExecutionException { MavenProject deployProject; if (null == m_project.getFile()) { deployProject = new MavenProject(); deployProject.setGroupId("examples"); deployProject.setArtifactId("pax-provision"); deployProject.setVersion("1.0-SNAPSHOT"); } else { deployProject = new MavenProject(m_project); } String internalId = PomUtils.getCompoundId(deployProject.getGroupId(), deployProject.getArtifactId()); deployProject.setGroupId(internalId + ".build"); deployProject.setArtifactId("deployment"); // remove unnecessary cruft deployProject.setPackaging("pom"); deployProject.getModel().setModules(null); deployProject.getModel().setDependencies(bundles); deployProject.getModel().setPluginRepositories(null); deployProject.getModel().setReporting(null); deployProject.setBuild(null); File deployFile = new File(deployProject.getBasedir(), "runner/deploy-pom.xml"); deployFile.getParentFile().mkdirs(); deployProject.setFile(deployFile); try { Writer writer = StreamFactory.newXmlWriter(deployFile); deployProject.writeModel(writer); IOUtil.close(writer); } catch (IOException e) { throw new MojoExecutionException("Unable to write deployment POM " + deployFile); } return deployProject; } /** * Install deployment POM in the local Maven repository * * @param project deployment project * @throws MojoExecutionException */ private void installDeploymentPom(MavenProject project) throws MojoExecutionException { String groupId = project.getGroupId(); String artifactId = project.getArtifactId(); String version = project.getVersion(); Artifact pomArtifact = m_factory.createProjectArtifact(groupId, artifactId, version); try { m_installer.install(project.getFile(), pomArtifact, m_localRepo); } catch (ArtifactInstallationException e) { throw new MojoExecutionException("Unable to install deployment POM " + pomArtifact); } } /** * Dynamically resolve and load the Pax-Runner class * * @param groupId pax-runner group id * @param artifactId pax-runner artifact id * @param mainClass main pax-runner classname * @param needClassifier classify pax-runner artifact according to current JVM * @return main pax-runner class * @throws MojoExecutionException */ private Class loadRunnerClass(String groupId, String artifactId, String mainClass, boolean needClassifier) throws MojoExecutionException { String jdk = null; if (needClassifier && System.getProperty("java.class.version").compareTo("49.0") < 0) { jdk = "jdk14"; } Artifact jarArtifact = m_factory.createArtifactWithClassifier(groupId, artifactId, runner, "jar", jdk); if (!PomUtils.downloadFile(jarArtifact, m_resolver, m_remoteRepos, m_localRepo)) { throw new MojoExecutionException("Unable to find Pax-Runner " + jarArtifact); } URL[] urls = new URL[1]; try { urls[0] = jarArtifact.getFile().toURI().toURL(); } catch (MalformedURLException e) { throw new MojoExecutionException("Bad Jar location " + jarArtifact.getFile()); } try { ClassLoader loader = new URLClassLoader(urls); Thread.currentThread().setContextClassLoader(loader); return Class.forName(mainClass, true, loader); } catch (ClassNotFoundException e) { throw new MojoExecutionException("Unable to find entry point " + mainClass + " in " + urls[0]); } } /** * Deploy bundles using the 'classic' Pax-Runner * * @param mainClass main Pax-Runner class * @param project deployment project * @param repositories comma separated list of Maven repositories * @throws MojoExecutionException */ private void deployRunnerClassic(Class mainClass, MavenProject project, String repositories) throws MojoExecutionException { String workDir = project.getBasedir() + "/runner"; String cachedPomName = project.getArtifactId() + '_' + project.getVersion() + ".pom"; File cachedPomFile = new File(workDir + "/lib/" + cachedPomName); // Force reload of pom cachedPomFile.delete(); if (PomUtils.isEmpty(framework)) { framework = "felix"; } String[] deployAppCmds = { "--dir=" + workDir, "--no-md5", "--platform=" + framework, "--profile=default", "--repository=" + repositories, "--localRepository=" + m_localRepo.getBasedir(), project.getGroupId(), project.getArtifactId(), project.getVersion() }; invokePaxRunner(mainClass, deployAppCmds); } /** * This method allows subclasses to specify additional * commands for provisioning. By default provide no initial * deploy commands. * @return A List of Strings that represent the deploy commands. */ protected List getDeployCommands() { return new ArrayList(); } /** * Deploy bundles using the new Pax-Runner codebase * * @param mainClass main Pax-Runner class * @param project deployment project * @param repositories comma separated list of Maven repositories * @throws MojoExecutionException */ private void deployRunnerNG(Class mainClass, MavenProject project, String repositories) throws MojoExecutionException { List deployAppCmds = getDeployCommands(); // only apply if explicitly configured if (PomUtils.isNotEmpty(framework)) { deployAppCmds.add("--platform=" + framework); } if (PomUtils.isNotEmpty(profiles)) { deployAppCmds.add("--profiles=" + profiles); } if (PomUtils.isNotEmpty(args)) { try { new URL(args); // check syntax } catch (MalformedURLException e) { // assume it's a local filename File argsFile = new File(args); args = argsFile.toURI().toString(); } // custom Pax-Runner arguments file deployAppCmds.add("--args=" + args); } // apply project provision settings before defaults deployAppCmds.addAll(Arrays.asList(provision)); // main deployment pom with project bundles as dependencies deployAppCmds.add(project.getFile().getAbsolutePath()); if (PomUtils.isNotEmpty(deployURLs)) { // additional (external) bundle URLs String[] urls = deployURLs.split(","); for (int i = 0; i < urls.length; i++) { deployAppCmds.add(urls[i].trim()); } } // use project settings to access remote/local repositories deployAppCmds.add("--localRepository=" + m_localRepo.getBasedir()); deployAppCmds.add("--repositories=" + repositories); deployAppCmds.add("--overwriteUserBundles"); getLog().debug("Starting Pax-Runner " + runner + " with: " + deployAppCmds.toString()); invokePaxRunner(mainClass, (String[]) deployAppCmds.toArray(new String[deployAppCmds.size()])); } /** * Invoke Pax-Runner in-process * * @param mainClass main Pax-Runner class * @param commands array of command-line options * @throws MojoExecutionException */ private void invokePaxRunner(Class mainClass, String[] commands) throws MojoExecutionException { Class[] paramTypes = new Class[1]; paramTypes[0] = String[].class; Object[] paramValues = new Object[1]; paramValues[0] = commands; try { Method entryPoint = mainClass.getMethod("main", paramTypes); entryPoint.invoke(null, paramValues); } catch (NoSuchMethodException e) { throw new MojoExecutionException("Unable to find Pax-Runner entry point"); } catch (IllegalAccessException e) { throw new MojoExecutionException("Unable to access Pax-Runner entry point"); } catch (InvocationTargetException e) { throw new MojoExecutionException("Pax-Runner exception", e); } } /** * @return backup OPS4J remote repository */ ArtifactRepository getOps4jRepository() { ArtifactRepositoryPolicy noSnapshots = new ArtifactRepositoryPolicy(false, ArtifactRepositoryPolicy.UPDATE_POLICY_DAILY, null); ArtifactRepositoryPolicy releases = new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_NEVER, null); return m_repoFactory.createArtifactRepository("ops4j.releases", "http://repository.ops4j.org/maven2/", m_defaultLayout, noSnapshots, releases); } }