| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| package org.apache.sling.maven.projectsupport; |
| |
| import static org.apache.sling.maven.projectsupport.BundleListUtils.interpolateProperties; |
| import static org.apache.sling.maven.projectsupport.BundleListUtils.readBundleList; |
| |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.util.Enumeration; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.shared.filtering.MavenFileFilter; |
| import org.apache.maven.shared.filtering.MavenFilteringException; |
| import org.apache.maven.shared.filtering.PropertyUtils; |
| import org.apache.sling.maven.projectsupport.BundleListUtils.ArtifactDefinitionsCallback; |
| import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList; |
| import org.codehaus.plexus.archiver.ArchiverException; |
| import org.codehaus.plexus.archiver.zip.ZipUnArchiver; |
| import org.codehaus.plexus.util.FileUtils; |
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
| import org.drools.KnowledgeBase; |
| import org.drools.KnowledgeBaseFactory; |
| import org.drools.builder.KnowledgeBuilder; |
| import org.drools.builder.KnowledgeBuilderError; |
| import org.drools.builder.KnowledgeBuilderFactory; |
| import org.drools.builder.ResourceType; |
| import org.drools.io.ResourceFactory; |
| import org.drools.runtime.StatefulKnowledgeSession; |
| |
| public abstract class AbstractUsingBundleListMojo extends AbstractBundleListMojo { |
| |
| /** |
| * JAR Packaging type. |
| */ |
| protected static final String JAR = "jar"; |
| |
| /** |
| * WAR Packaging type. |
| */ |
| protected static final String WAR = "war"; |
| |
| protected static final String CONFIG_PATH_PREFIX = "resources/config"; |
| |
| protected static final String BUNDLE_PATH_PREFIX = "resources/bundles"; |
| |
| protected static boolean shouldCopy(File source, File dest) { |
| if (!dest.exists()) { |
| return true; |
| } |
| return source.lastModified() > dest.lastModified(); |
| } |
| |
| /** |
| * The definition of the defaultBundleList artifact. |
| * |
| * @parameter |
| */ |
| protected ArtifactDefinition defaultBundleList; |
| |
| /** |
| * Any additional bundles to include in the project's bundles directory. |
| * |
| * @parameter |
| */ |
| private ArtifactDefinition[] additionalBundles; |
| |
| private BundleList initializedBundleList; |
| |
| /** |
| * Bundles which should be removed from the project's bundles directory. |
| * |
| * @parameter |
| */ |
| private ArtifactDefinition[] bundleExclusions; |
| |
| /** |
| * If true, include the default bundles. |
| * |
| * @parameter expression="${includeDefaultBundles}" default-value="true" |
| */ |
| private boolean includeDefaultBundles; |
| |
| /** |
| * @parameter |
| */ |
| private File[] rewriteRuleFiles; |
| |
| /** |
| * The list of tokens to include when copying configs |
| * from partial bundle lists. |
| * |
| * @parameter default-value="**" |
| */ |
| private String[] configIncludes; |
| |
| /** |
| * The list of tokens to exclude when copying the configs |
| * from partial bundle lists. |
| * |
| * @parameter |
| */ |
| private String[] configExcludes; |
| |
| /** |
| * The list of names to exclude when copying properties |
| * from partial bundle lists. |
| * |
| * @parameter |
| */ |
| private String[] propertiesExcludes; |
| |
| /** |
| * @component |
| */ |
| protected MavenFileFilter mavenFileFilter; |
| |
| /** |
| * The zip unarchiver. |
| * |
| * @component role="org.codehaus.plexus.archiver.UnArchiver" roleHint="zip" |
| */ |
| private ZipUnArchiver zipUnarchiver; |
| |
| private Properties slingProperties; |
| |
| private Properties slingWebappProperties; |
| |
| private Properties slingStandaloneProperties; |
| |
| private String slingBootstrapCommand; |
| |
| private String slingWebappBootstrapCommand; |
| |
| private String slingStandaloneBootstrapCommand; |
| |
| /** |
| * @parameter default-value="${project.build.directory}/tmpBundleListconfig" |
| */ |
| private File tmpOutputDir; |
| |
| /** |
| * @parameter default-value="${project.build.directory}/tmpConfigDir" |
| */ |
| private File tempConfigDir; |
| |
| private File overlayConfigDir; |
| |
| public final void execute() throws MojoFailureException, MojoExecutionException { |
| try { |
| initBundleList(); |
| extractConfigurations(); |
| } catch (MojoExecutionException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new MojoExecutionException("Unable to load dependency information from properties file.", e); |
| } |
| executeWithArtifacts(); |
| |
| } |
| |
| @Override |
| protected File getConfigDirectory() { |
| if ( this.overlayConfigDir != null ) { |
| return this.overlayConfigDir; |
| } |
| return super.getConfigDirectory(); |
| } |
| |
| /** |
| * Execute the logic of the plugin after the default artifacts have been |
| * initialized. |
| */ |
| protected abstract void executeWithArtifacts() throws MojoExecutionException, MojoFailureException; |
| |
| protected BundleList getInitializedBundleList() { |
| return initializedBundleList; |
| } |
| |
| /** |
| * Hook methods for subclasses to initialize any additional artifact |
| * definitions. |
| * |
| * @param dependencies the dependency properties loaded from the JAR file |
| */ |
| protected void initArtifactDefinitions(Properties dependencies) { |
| } |
| |
| /** |
| * Hook methods for subclasses to initialize the bundle list. |
| */ |
| protected void initBundleList(BundleList bundleList) { |
| } |
| |
| /** |
| * Initialize the artifact definitions using defaults inside the plugin JAR. |
| * |
| * @throws IOException if the default properties can't be read |
| * @throws XmlPullParserException |
| * @throws MojoExecutionException |
| */ |
| private final void initArtifactDefinitions() throws IOException { |
| BundleListUtils.initArtifactDefinitions(getClass().getClassLoader(), new ArtifactDefinitionsCallback() { |
| |
| public void initArtifactDefinitions(Properties dependencies) { |
| if (defaultBundleList == null) { |
| defaultBundleList = new ArtifactDefinition(); |
| } |
| defaultBundleList.initDefaults(dependencies.getProperty("defaultBundleList")); |
| |
| AbstractUsingBundleListMojo.this.initArtifactDefinitions(dependencies); |
| } |
| }); |
| } |
| |
| private final void initBundleList() throws IOException, XmlPullParserException, MojoExecutionException { |
| initArtifactDefinitions(); |
| if (BundleListUtils.isCurrentArtifact(project, defaultBundleList)) { |
| initializedBundleList = readBundleList(bundleListFile); |
| } else { |
| initializedBundleList = new BundleList(); |
| if (includeDefaultBundles) { |
| Artifact defBndListArtifact = getArtifact(defaultBundleList.getGroupId(), |
| defaultBundleList.getArtifactId(), defaultBundleList.getVersion(), defaultBundleList.getType(), |
| defaultBundleList.getClassifier()); |
| getLog().info("Using bundle list file from " + defBndListArtifact.getFile().getAbsolutePath()); |
| initializedBundleList = readBundleList(defBndListArtifact.getFile()); |
| } |
| |
| if (bundleListFile.exists()) { |
| initializedBundleList.merge(readBundleList(bundleListFile)); |
| } |
| } |
| // add additional bundles |
| if (additionalBundles != null) { |
| for (ArtifactDefinition def : additionalBundles) { |
| initializedBundleList.add(def.toBundle()); |
| } |
| } |
| |
| // check for partial bundle lists |
| final Set<Artifact> dependencies = project.getDependencyArtifacts(); |
| for (Artifact artifact : dependencies) { |
| if (PARTIAL.equals(artifact.getType())) { |
| getLog().info( |
| String.format("Merging partial bundle list %s:%s:%s", artifact.getGroupId(), |
| artifact.getArtifactId(), artifact.getVersion())); |
| initializedBundleList.merge(readBundleList(artifact.getFile())); |
| } |
| } |
| |
| // handle exclusions |
| if (bundleExclusions != null) { |
| for (ArtifactDefinition def : bundleExclusions) { |
| initializedBundleList.remove(def.toBundle(), false); |
| } |
| } |
| |
| initBundleList(initializedBundleList); |
| |
| interpolateProperties(initializedBundleList, project, mavenSession); |
| |
| rewriteBundleList(initializedBundleList); |
| } |
| |
| private final void extractConfigurations() throws MojoExecutionException, IOException { |
| final Set<Artifact> dependencies = project.getDependencyArtifacts(); |
| for (Artifact artifact : dependencies) { |
| if (PARTIAL.equals(artifact.getType())) { |
| extractConfiguration(artifact); |
| } |
| } |
| // copy own config files |
| if ( this.overlayConfigDir != null && super.getConfigDirectory().exists() ) { |
| copyDirectory(super.getConfigDirectory(), this.overlayConfigDir, null, FileUtils.getDefaultExcludes()); |
| } |
| } |
| |
| private void extractConfiguration(final Artifact artifact) throws MojoExecutionException, IOException { |
| // check for configuration artifact |
| Artifact cfgArtifact = null; |
| try { |
| cfgArtifact = getArtifact(artifact.getGroupId(), |
| artifact.getArtifactId(), |
| artifact.getVersion(), |
| AttachPartialBundleListMojo.CONFIG_TYPE, |
| AttachPartialBundleListMojo.CONFIG_CLASSIFIER); |
| } catch (final MojoExecutionException ignore) { |
| // we just ignore this |
| } |
| if ( cfgArtifact != null ) { |
| getLog().info( |
| String.format("Merging settings from partial bundle list %s:%s:%s", cfgArtifact.getGroupId(), |
| cfgArtifact.getArtifactId(), cfgArtifact.getVersion())); |
| |
| // extract |
| zipUnarchiver.setSourceFile(cfgArtifact.getFile()); |
| try { |
| this.tmpOutputDir.mkdirs(); |
| zipUnarchiver.setDestDirectory(this.tmpOutputDir); |
| zipUnarchiver.extract(); |
| |
| final File slingDir = new File(this.tmpOutputDir, "sling"); |
| this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_COMMON_PROPS), 0); |
| this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_PROPS), 1); |
| this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_PROPS), 2); |
| this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_COMMON_BOOTSTRAP), 0); |
| this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_BOOTSTRAP), 1); |
| this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_BOOTSTRAP), 2); |
| |
| // and now configurations |
| final File configDir = new File(this.tmpOutputDir, "config"); |
| if ( configDir.exists() ) { |
| if ( this.overlayConfigDir == null ) { |
| this.tempConfigDir.mkdirs(); |
| this.overlayConfigDir = this.tempConfigDir; |
| } |
| final String[] defaultExcludes = FileUtils.getDefaultExcludes(); |
| String[] excludes; |
| if ( this.configExcludes != null ) { |
| excludes = new String[defaultExcludes.length + this.configExcludes.length]; |
| System.arraycopy(defaultExcludes, 0, excludes, 0, defaultExcludes.length); |
| System.arraycopy(this.configExcludes, 0, excludes, defaultExcludes.length, this.configExcludes.length); |
| } else { |
| excludes = defaultExcludes; |
| } |
| String[] includes = null; |
| if ( this.configIncludes != null ) { |
| includes = this.configIncludes; |
| } |
| copyDirectory(configDir, this.overlayConfigDir, |
| includes, excludes); |
| } |
| } catch (final ArchiverException ae) { |
| throw new MojoExecutionException("Unable to extract configuration archive.",ae); |
| } finally { |
| // and delete at the end |
| FileUtils.deleteDirectory(this.tmpOutputDir); |
| } |
| } |
| } |
| |
| private void rewriteBundleList(BundleList bundleList) throws MojoExecutionException { |
| if (rewriteRuleFiles != null) { |
| KnowledgeBase knowledgeBase = createKnowledgeBase(rewriteRuleFiles); |
| StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession(); |
| try { |
| session.setGlobal("mavenSession", mavenSession); |
| session.setGlobal("mavenProject", project); |
| session.insert(bundleList); |
| session.fireAllRules(); |
| } finally { |
| session.dispose(); |
| } |
| } |
| } |
| |
| private KnowledgeBase createKnowledgeBase(File[] files) throws MojoExecutionException { |
| KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(); |
| builder.add(ResourceFactory.newClassPathResource("drools-globals.drl", getClass()), ResourceType.DRL); |
| for (File file : files) { |
| getLog().info("Parsing rule file " + file.getAbsolutePath()); |
| builder.add(ResourceFactory.newFileResource(file), ResourceType.DRL); |
| } |
| if (builder.hasErrors()) { |
| getLog().error("Rule errors:"); |
| for (KnowledgeBuilderError error : builder.getErrors()) { |
| getLog().error(error.toString()); |
| } |
| throw new MojoExecutionException("Unable to create rules. See log for details."); |
| } |
| |
| KnowledgeBase base = KnowledgeBaseFactory.newKnowledgeBase(); |
| base.addKnowledgePackages(builder.getKnowledgePackages()); |
| return base; |
| } |
| |
| private void copyProperties(final Properties source, final Properties dest) { |
| final Enumeration<Object> keys = source.keys(); |
| while ( keys.hasMoreElements() ) { |
| final Object key = keys.nextElement(); |
| dest.put(key, source.get(key)); |
| } |
| } |
| |
| private void readSlingProperties(final File propsFile, final int mode) throws MojoExecutionException { |
| if (propsFile.exists()) { |
| File tmp = null; |
| try { |
| tmp = File.createTempFile("sling", "props"); |
| mavenFileFilter.copyFile(propsFile, tmp, true, project, null, true, |
| System.getProperty("file.encoding"), mavenSession); |
| final Properties loadedProps = PropertyUtils.loadPropertyFile(tmp, null); |
| if ( mode == 0 ) { |
| if ( this.slingProperties == null ) { |
| this.slingProperties = loadedProps; |
| } else { |
| this.copyProperties(loadedProps, this.slingProperties); |
| } |
| filterProperties(this.slingProperties); |
| } else if ( mode == 1 ) { |
| if ( this.slingWebappProperties == null ) { |
| this.slingWebappProperties = loadedProps; |
| } else { |
| this.copyProperties(loadedProps, this.slingWebappProperties); |
| } |
| filterProperties(this.slingWebappProperties); |
| } else { |
| if ( this.slingStandaloneProperties == null ) { |
| this.slingStandaloneProperties = loadedProps; |
| } else { |
| this.copyProperties(loadedProps, this.slingStandaloneProperties); |
| } |
| filterProperties(this.slingStandaloneProperties); |
| } |
| } catch (IOException e) { |
| throw new MojoExecutionException("Unable to create filtered properties file", e); |
| } catch (MavenFilteringException e) { |
| throw new MojoExecutionException("Unable to create filtered properties file", e); |
| } finally { |
| if (tmp != null) { |
| tmp.delete(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Filter properties by removing excluded properties |
| */ |
| private void filterProperties(final Properties props) { |
| if ( this.propertiesExcludes != null ) { |
| for(final String name : this.propertiesExcludes) { |
| props.remove(name.trim()); |
| } |
| } |
| } |
| |
| protected Properties getSlingProperties(final boolean standalone) throws MojoExecutionException { |
| readSlingProperties(this.commonSlingProps, 0); |
| final Properties additionalProps = (standalone ? this.slingStandaloneProperties : this.slingWebappProperties); |
| if ( this.slingProperties == null) { |
| return additionalProps; |
| } |
| if ( additionalProps != null ) { |
| final Properties combinedProps = new Properties(); |
| this.copyProperties(this.slingProperties, combinedProps); |
| this.copyProperties(additionalProps, combinedProps); |
| return combinedProps; |
| } |
| return this.slingProperties; |
| } |
| |
| /** |
| * Try to read the bootstrap command file |
| * The filter is copied to a tmp location to apply filtering. |
| * @throws MojoExecutionException |
| */ |
| private void readSlingBootstrap(final File bootstrapFile, final int mode) throws MojoExecutionException { |
| if (bootstrapFile.exists()) { |
| File tmp = null; |
| Reader reader = null; |
| try { |
| tmp = File.createTempFile("sling", "bootstrap"); |
| mavenFileFilter.copyFile(bootstrapFile, tmp, true, project, null, true, |
| System.getProperty("file.encoding"), mavenSession); |
| reader = new FileReader(tmp); |
| final StringBuilder sb = new StringBuilder(); |
| if ( mode == 0 ) { |
| if ( this.slingBootstrapCommand != null ) { |
| sb.append(this.slingBootstrapCommand); |
| } |
| } else if ( mode == 1 ) { |
| if ( this.slingWebappBootstrapCommand != null ) { |
| sb.append(this.slingWebappBootstrapCommand); |
| } |
| } else { |
| if ( this.slingStandaloneBootstrapCommand != null ) { |
| sb.append(this.slingStandaloneBootstrapCommand); |
| } |
| } |
| final char[] buffer = new char[2048]; |
| int l; |
| while ( (l = reader.read(buffer, 0, buffer.length) ) != -1 ) { |
| sb.append(buffer, 0, l); |
| } |
| sb.append('\n'); |
| if ( mode == 0 ) { |
| this.slingBootstrapCommand = sb.toString(); |
| } else if ( mode == 1 ) { |
| this.slingWebappBootstrapCommand = sb.toString(); |
| } else { |
| this.slingStandaloneBootstrapCommand = sb.toString(); |
| } |
| } catch (final IOException e) { |
| throw new MojoExecutionException("Unable to create filtered bootstrap file", e); |
| } catch (final MavenFilteringException e) { |
| throw new MojoExecutionException("Unable to create filtered bootstrap file", e); |
| } finally { |
| if (tmp != null) { |
| tmp.delete(); |
| } |
| if ( reader != null ) { |
| try { |
| reader.close(); |
| } catch (final IOException ignore) {} |
| } |
| } |
| } |
| } |
| |
| /** |
| * Try to read the bootstrap command file and return its content |
| * The filter is copied to a tmp location to apply filtering. |
| * @return The contents are <code>null</code> |
| * @throws MojoExecutionException |
| */ |
| protected String getSlingBootstrap(final boolean standalone) throws MojoExecutionException { |
| this.readSlingBootstrap(this.commonSlingBootstrap, 0); |
| final String addCmds = (standalone ? this.slingStandaloneBootstrapCommand : this.slingWebappBootstrapCommand); |
| if ( this.slingBootstrapCommand == null ) { |
| return addCmds; |
| } |
| if ( addCmds != null ) { |
| final StringBuilder builder = new StringBuilder(this.slingBootstrapCommand); |
| builder.append('\n'); |
| builder.append(addCmds); |
| return builder.toString(); |
| } |
| return this.slingBootstrapCommand; |
| } |
| } |