Package fr.imag.adele.apam.maven.plugin

Source Code of fr.imag.adele.apam.maven.plugin.OBRGeneratorMojo

/**
* Copyright 2011-2012 Universite Joseph Fourier, LIG, ADELE team
*   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.
*/
package fr.imag.adele.apam.maven.plugin;

import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.apache.felix.bundlerepository.RepositoryAdmin;
import org.apache.felix.bundlerepository.impl.RepositoryAdminImpl;
import org.apache.felix.ipojo.manipulator.render.MetadataRenderer;
import org.apache.felix.ipojo.manipulator.store.JarFileResourceStore;
import org.apache.felix.ipojo.manipulator.store.builder.DefaultManifestBuilder;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.parser.ManifestMetadataParser;
import org.apache.felix.ipojo.plugin.ManipulatorMojo;
import org.apache.felix.utils.log.Logger;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceListener;

import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.declarations.AtomicImplementationDeclaration;
import fr.imag.adele.apam.declarations.ComponentDeclaration;
import fr.imag.adele.apam.declarations.CompositeDeclaration;
import fr.imag.adele.apam.declarations.InstanceDeclaration;
import fr.imag.adele.apam.declarations.PropertyDefinition;
import fr.imag.adele.apam.declarations.Reporter;
import fr.imag.adele.apam.declarations.SpecificationDeclaration;
import fr.imag.adele.apam.declarations.repository.RepositoryChain;
import fr.imag.adele.apam.declarations.repository.acr.ApamComponentRepository;
import fr.imag.adele.apam.declarations.repository.maven.MavenProjectRepository;
import fr.imag.adele.apam.maven.plugin.helpers.EnrichElementsHelper;
import fr.imag.adele.apam.maven.plugin.validation.ValidationContext;
import fr.imag.adele.apam.maven.plugin.validation.Validator;
import fr.imag.adele.apam.util.ApamMavenProperties;

/**
* Packages an OSGi jar "iPOJO bundle" as an "APAM bundle".
*
* @version $Rev$, $Date$
* @extendsPlugin maven-ipojo-plugin
* @goal apam-bundle
* @extendsGoal ipojo-bundle
* @requiresrelationResolution runtime
* @description manipulate an OSGi bundle jar to include the obr.xml file and
*              build APAM bundle
*
* @author ApAM Team
*/
public class OBRGeneratorMojo extends ManipulatorMojo  {

    /**
     * ACR Repository (ApAM Component Repository)
     * used as input (read only the existing component)
     * @parameter
     */
    private String[] inputAcr;

    /**
     * ACR Repository (ApAM Component Repository)
     * used as output (write the current component)
     * @parameter property="outputAcr"
     */
    private String outputAcr;

    private static final String NONE = "NONE";
   
    /**
     * Local Repository.
     *
     * @parameter default-value="${localRepository}"
     * @required
     * @readonly
     */
    private ArtifactRepository localRepository;

    private static final String DEFAULT_OBR_XML = "repository.xml";


    private List<URL> getInputRepositoryLocations() {
     
        List<URL> acrLocations  = new ArrayList<URL>();

        if (inputAcr == null || inputAcr.length == 0) {
           
          URL location      = null;
         
            if (outputAcr != null) {
                getLog().info("No inputAcr repository URL specified, first fallback, trying to use the target output ACR");
                location = getURL(outputAcr);

            }
           
            if (location != null)
              return Collections.singletonList(location);
           
            getLog().info("No inputAcr repository URL specified, using default local maven repository (obr) at "+localRepository.getUrl());
          location = getURL(localRepository.getUrl()+DEFAULT_OBR_XML);

            if (location != null)
              return Collections.singletonList(location);
         
            return null;
        }
        else {
          for (String input : inputAcr) {
              URL location = (input != null || !NONE.equals(input)) ? getURL(input) : null;
                 if (location != null)
                   acrLocations.add(location);
      }
        }
       
        return acrLocations;
    }


    private URL getURL(String location) {
     
      if (location == null)
        return null;
     
        try {

          URI locationURI = ACRInstallMojo.getTargetACR(location);
          if (locationURI == null) {
            getLog().info("Invalid repository URL specified : " + location +", will be ignored");
            return null;
          }
         
          return locationURI.toURL();
    }
        catch(MalformedURLException exc) {
        getLog().info("Invalid repository URL specified : " + location +", will be ignored");
        return null;
    }
    }
   
    /**
     * mojo boolean property includeMavenDependencies
     * = true means that initial external ApamCapabilities are built from dependencies in the pom (default)
     * = false means that all apam-component external dependencies will only be resolved using the inputAcr
     *
     * @parameter property="includeMavenDependencies" default-value="true"
     */
    private Boolean includeMavenDependencies;

  /**
   * The Maven project.
   *
   * @parameter default-value="${project}"
   * @required
   * @readonly
   */
  private MavenProject project;

 
  /**
   * @parameter default-value="${basedir}
   */
  private File baseDirectory;




    /**
     * This utility class capture messages and errors during processing and show them in the
     * build log
     *
     * TODO Built a better structured error report
     *
     * @author vega
     *
     */
    private static class ErrorReport implements Reporter {
     
      private final Log log;
      private boolean hasErrors = false;
     
      public ErrorReport(Log log) {
        this.log     = log;
        this.hasErrors  = false;
      }
     
      public boolean hasErrors() {
        return hasErrors;
      }
     
      @Override
      public void report(Severity severity, String message) {
        switch (severity) {
        case ERROR :
          log.error(message);
          hasErrors = true;
          break;
        case WARNING :
        case SUSPECT :
          log.warn(message);
          break;
        case INFO :
          log.info(message);
          break;
        }
      }
     
    }
   
    /**
   * Execute method : this method launches the OBR generation.
   *
   * @throws MojoExecutionException
   *             : an exception occurs during the OBR generation..
   *
   */
  public void execute() throws MojoExecutionException {

        try {

          /*
           * Perform iPOJO manipulation
           */
      super.execute();

      /*
       * Loads and parses components from the project and its dependencies
       */
     
      ErrorReport parsingResult = new ErrorReport(getLog());
      MavenProjectRepository projectRepository = new MavenProjectRepository(project, includeMavenDependencies, ApamMavenProperties.mavenVersion, parsingResult);

      getLog().info("ApAM metadata manipulator");
     
      /*
       * The components to verify
       */
     
      List<ComponentDeclaration> components = projectRepository.getBuildRepository().getComponents();
      if (components.isEmpty()) {
        throw new InvalidApamMetadataException("No Apam metadata");
      }

      ErrorReport acrParsingResult = new ErrorReport(getLog());
        ApamComponentRepository acr = null;
            try {
                acr = new ApamComponentRepository(mockManager(getInputRepositoryLocations()),getInputRepositoryLocations(),acrParsingResult);
            } catch (Exception exc) {
                exc.printStackTrace();
                throw new MojoExecutionException("Exception during initialize of OBR/ACR repositories "+exc.getMessage());
            }
           
            /*
             * Validate components, we validate first the most abstract components so that if there are cross-references
             * among components in the same build we detect errors soon and avoid cascaded errors
             */

            ValidationContext context  = new ValidationContext(new RepositoryChain(projectRepository,acr));
        Validator validator      = new Validator(projectRepository.getClasspath(),context);
       
            ErrorReport validatorResult = new ErrorReport(getLog());
           
      for (ComponentDeclaration component : components) {
        if (component instanceof SpecificationDeclaration) {
          validator.validate(component, validatorResult);
        }
      }

      for (ComponentDeclaration component : components) {
        if (component instanceof AtomicImplementationDeclaration) {
          validator.validate(component, validatorResult);
        }
      }

      for (ComponentDeclaration component : components) {
        if (component instanceof CompositeDeclaration) {
          validator.validate(component, validatorResult);
        }
      }

      for (ComponentDeclaration component : components) {
        if (component instanceof InstanceDeclaration) {
          validator.validate(component, validatorResult);
        }
      }

      /*
       * Abort if there are errors
       */
      if (parsingResult.hasErrors()) {
        throw new MojoFailureException("Invalid xml Apam Metadata syntax.");
      }
     
      if (validatorResult.hasErrors()) {
        throw new MojoFailureException("Invalid Apam component declaration");
      }
     
      /*
       * Generate the OBR metadata corresponding to this project
       */

      ApamComponentRepositoryBuilder builder  = new ApamComponentRepositoryBuilder(acr);

            ErrorReport generatorResult = new ErrorReport(getLog());
            String obrProjectContent  = builder.build(context,projectRepository,generatorResult);

      if (generatorResult.hasErrors()) {
        throw new MojoFailureException("Error generating ACR metadata");
      }

      /*
       * Modify the OBR file that will be merged by the felix maven plugin at install
       * time to modify the output repository
       *
       * TODO We need to be sure whether the file used at install is in src or target
       * directory
       */
      OutputStream obr;
      String obrFileStr = baseDirectory.getAbsolutePath()
          + File.separator + "src" + File.separator + "main"
          + File.separator + "resources" + File.separator + "obr.xml";
      File obrFile = new File(obrFileStr);

      // maven ?? copies first in target/classes before to look in
      // src/resources
      // and copies src/resources/obr.xml to target/classes *after* obr
      // modification
      // Thus we delete first target/classes/obr.xml to be sure the newly
      // generated obr.xml file will be used

      String oldObrFileStr = baseDirectory.getAbsolutePath()
          + File.separator + "target" + File.separator + "classes"
          + File.separator + "obr.xml";
      File oldObrFile = new File(oldObrFileStr);
      if (oldObrFile.exists()) {
        oldObrFile.delete();
      }

      if (!obrFile.exists()) {
        obrFile.getParentFile().mkdirs();
      }
      obr = new FileOutputStream(obrFile);
      obr.write(obrProjectContent.getBytes());
      obr.flush();

      // Map<String, Element> map =
      // ObrAdditionalProperties.parseFile(obrFile,getLog());
      // System.err.println("Obr file : " + obrFile.getAbsolutePath());
      obr.close();

      updateJarFile();

    } catch (Exception e) {
      getLog().error(e.getMessage(), e);
      throw new MojoExecutionException(e.getMessage());
    }

    getLog().info(" obr.xml File generation - SUCCESS ");
  }

     /**
     * Mock some of the OSGi context to allow using the repository at build time
     */

    private static RepositoryAdmin mockManager(List<URL> repositories) throws Exception {
     
      if (repositories == null || repositories.isEmpty())
        return null;
     
        BundleContext bundleContext = mock(BundleContext.class);
        Bundle systemBundle = mock(Bundle.class);

        // TODO: Change this one
        when(bundleContext.getProperty(RepositoryAdminImpl.REPOSITORY_URL_PROP)).thenReturn(repositories.get(0).toExternalForm());

        when(bundleContext.getProperty(anyString())).thenReturn(null);
        when(bundleContext.getBundle(0)).thenReturn(systemBundle);
        when(systemBundle.getHeaders()).thenReturn(new Hashtable<String,String>());
        when(systemBundle.getRegisteredServices()).thenReturn(null);
        when(new Long(systemBundle.getBundleId())).thenReturn(new Long(0));
        when(systemBundle.getBundleContext()).thenReturn(bundleContext);
        bundleContext.addBundleListener((BundleListener) anyObject());
        bundleContext.addServiceListener((ServiceListener) anyObject());
        when(bundleContext.getBundles()).thenReturn(new Bundle[]{systemBundle});

        RepositoryAdminImpl repoAdmin = new RepositoryAdminImpl(bundleContext, new Logger(bundleContext));

        // force initialization && remove all initial repositories
        org.apache.felix.bundlerepository.Repository[] repos = repoAdmin.listRepositories();
        for (int i = 0; repos != null && i < repos.length; i++) {
            repoAdmin.removeRepository(repos[i].getURI());
        }

        return repoAdmin;
    }

  public void updateJarFile() throws MojoExecutionException {
     
    File newOutput = new File(baseDirectory.getAbsolutePath()+ File.separator + "target" + File.separator + "_temp.jar");
    if (newOutput.exists()) {
      newOutput.delete();
    }

    JarFile bundle        = null;
    JarFileResourceStore store   = null;
   
    try {
   
      Artifact artifact   = project.getArtifact();
      if (artifact.getFile() == null || !artifact.getFile().exists() || !artifact.getFile().isFile()) {
        throw new IOException("Error loading jar file for maven artifact "+artifact.getId());
      }

      bundle  = new JarFile(artifact.getFile());
      store   = new JarFileResourceStore(bundle,newOutput);
     
      Manifest manifest = bundle.getManifest();
      String componentHeader = manifest.getMainAttributes().getValue("iPOJO-Components");
      if (componentHeader == null) {
        return;
      }

      Element metadata = ManifestMetadataParser.parseHeaderMetadata(componentHeader);
      store.setManifest(bundle.getManifest());

     
      ComponentDeclaration template = getVersionedComponentTemplate(artifact);
      EnrichElementsHelper.addPropertiesToChildrenApAMComponents(metadata, template.getPropertyDefinitions(), template.getProperties());

      DefaultManifestBuilder builder = new DefaultManifestBuilder();
      builder.setMetadataRenderer(new MetadataRenderer());
     
      builder.addMetada(Arrays.asList(metadata.getElements()));
     
      store.setManifestBuilder(builder);

    } catch (Exception e) {
      getLog().error(e.getMessage(), e);
      throw new MojoExecutionException(e.getMessage());
    }
    finally {
      try {
        if (store != null)
          store.close();
      }
      catch(Exception ignored) {
      }
      try {
        if (bundle != null)
          bundle.close();
      }
      catch(Exception ignored) {
      }
     
    }

    project.getArtifact().getFile().delete();
    newOutput.renameTo(project.getArtifact().getFile());
   
  }

 
  public static final String PROPERTY_VERSION_APAM       = "apam.version";
  public static final String PROPERTY_VERSION_MAVEN_GROUP   = "maven.groupId";
  public static final String PROPERTY_VERSION_MAVEN_ARTIFACT   = "maven.artifactId";
  public static final String PROPERTY_VERSION_MAVEN_VERSION  = "maven.version";
 
  private static final ComponentDeclaration getVersionedComponentTemplate(Artifact artifact) {
   
    SpecificationDeclaration template = new SpecificationDeclaration("template");
   
    addProperty(template,PROPERTY_VERSION_APAM,"version",ApamMavenProperties.mavenVersion.replace('-', '.'));

    addProperty(template,PROPERTY_VERSION_MAVEN_GROUP,"string",artifact.getGroupId());
    addProperty(template,PROPERTY_VERSION_MAVEN_ARTIFACT,"string",artifact.getArtifactId());
    addProperty(template,PROPERTY_VERSION_MAVEN_VERSION,"string",artifact.getVersion());
   
    addProperty(template,CST.VERSION,"version",artifact.getVersion().replace('-', '.'));
   
    return template;
  }
 
  /**
   * Add a property to an existing component
   *
   * NOTE We may be modifying a component that has already version information attached (either because the
   * component has already been built, and we are loading it as a dependency, or because the user has added
   * the information manually) so we need to be careful not to override it
   *
   */
  private static final void addProperty(ComponentDeclaration component, String property, String type, String value) {
   
    /*
     */
    PropertyDefinition defintition = component.getPropertyDefinition(property);
    if (defintition == null) {
      defintition = new PropertyDefinition(component.getReference(), property, type, null);
      component.getPropertyDefinitions().add(defintition);
    }

    component.getProperties().put(property, value);
  }
 
}
TOP

Related Classes of fr.imag.adele.apam.maven.plugin.OBRGeneratorMojo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.