1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.felix.bundleplugin;
20  
21  
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.OutputStream;
27  import java.util.LinkedHashMap;
28  import java.util.Map;
29  import java.util.Map.Entry;
30  import java.util.Properties;
31  import java.util.jar.Manifest;
32  
33  import org.apache.maven.plugin.MojoExecutionException;
34  import org.apache.maven.plugin.MojoFailureException;
35  import org.apache.maven.project.MavenProject;
36  
37  import aQute.lib.osgi.Analyzer;
38  import aQute.lib.osgi.Builder;
39  import aQute.lib.osgi.Jar;
40  import aQute.lib.osgi.Resource;
41  
42  
43  /**
44   * Generate an OSGi manifest for this project
45   * 
46   * @goal manifest
47   * @phase process-classes
48   * @requiresDependencyResolution test
49   * @threadSafe
50   */
51  public class ManifestPlugin extends BundlePlugin
52  {
53      /**
54       * When true, generate the manifest by rebuilding the full bundle in memory 
55       *
56       * @parameter expression="${rebuildBundle}"
57       */
58      protected boolean rebuildBundle;
59  
60  
61      @Override
62      protected void execute( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
63          throws MojoExecutionException
64      {
65          Manifest manifest;
66          try
67          {
68              manifest = getManifest( project, instructions, properties, classpath );
69          }
70          catch ( FileNotFoundException e )
71          {
72              throw new MojoExecutionException( "Cannot find " + e.getMessage()
73                  + " (manifest goal must be run after compile phase)", e );
74          }
75          catch ( IOException e )
76          {
77              throw new MojoExecutionException( "Error trying to generate Manifest", e );
78          }
79          catch ( MojoFailureException e )
80          {
81              getLog().error( e.getLocalizedMessage() );
82              throw new MojoExecutionException( "Error(s) found in manifest configuration", e );
83          }
84          catch ( Exception e )
85          {
86              getLog().error( "An internal error occurred", e );
87              throw new MojoExecutionException( "Internal error in maven-bundle-plugin", e );
88          }
89  
90          File outputFile = new File( manifestLocation, "MANIFEST.MF" );
91  
92          try
93          {
94              writeManifest( manifest, outputFile );
95          }
96          catch ( IOException e )
97          {
98              throw new MojoExecutionException( "Error trying to write Manifest to file " + outputFile, e );
99          }
100     }
101 
102 
103     public Manifest getManifest( MavenProject project, Jar[] classpath ) throws IOException, MojoFailureException,
104         MojoExecutionException, Exception
105     {
106         return getManifest( project, new LinkedHashMap(), new Properties(), classpath );
107     }
108 
109 
110     public Manifest getManifest( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
111         throws IOException, MojoFailureException, MojoExecutionException, Exception
112     {
113         Analyzer analyzer = getAnalyzer( project, instructions, properties, classpath );
114         boolean hasErrors = reportErrors( "Manifest " + project.getArtifact(), analyzer );
115         if ( hasErrors )
116         {
117             String failok = analyzer.getProperty( "-failok" );
118             if ( null == failok || "false".equalsIgnoreCase( failok ) )
119             {
120                 throw new MojoFailureException( "Error(s) found in manifest configuration" );
121             }
122         }
123 
124         Jar jar = analyzer.getJar();
125 
126         if ( unpackBundle )
127         {
128             File outputFile = getOutputDirectory();
129             for ( Entry<String, Resource> entry : jar.getResources().entrySet() )
130             {
131                 File entryFile = new File( outputFile, entry.getKey() );
132                 if ( !entryFile.exists() || entry.getValue().lastModified() == 0 )
133                 {
134                     entryFile.getParentFile().mkdirs();
135                     OutputStream os = new FileOutputStream( entryFile );
136                     entry.getValue().write( os );
137                     os.close();
138                 }
139             }
140         }
141 
142         Manifest manifest = jar.getManifest();
143 
144         // cleanup...
145         analyzer.close();
146 
147         return manifest;
148     }
149 
150 
151     protected Analyzer getAnalyzer( MavenProject project, Jar[] classpath ) throws IOException, MojoExecutionException,
152         Exception
153     {
154         return getAnalyzer( project, new LinkedHashMap(), new Properties(), classpath );
155     }
156 
157 
158     protected Analyzer getAnalyzer( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
159         throws IOException, MojoExecutionException, Exception
160     {
161         if ( rebuildBundle && supportedProjectTypes.contains( project.getArtifact().getType() ) )
162         {
163             return buildOSGiBundle( project, instructions, properties, classpath );
164         }
165 
166         File file = project.getArtifact().getFile();
167         if ( file == null )
168         {
169             file = getOutputDirectory();
170         }
171 
172         if ( !file.exists() )
173         {
174             throw new FileNotFoundException( file.getPath() );
175         }
176 
177         Builder analyzer = getOSGiBuilder( project, instructions, properties, classpath );
178 
179         analyzer.setJar( file );
180 
181         if ( analyzer.getProperty( Analyzer.EXPORT_PACKAGE ) == null
182             && analyzer.getProperty( Analyzer.EXPORT_CONTENTS ) == null
183             && analyzer.getProperty( Analyzer.PRIVATE_PACKAGE ) == null )
184         {
185             String export = analyzer.calculateExportsFromContents( analyzer.getJar() );
186             analyzer.setProperty( Analyzer.EXPORT_PACKAGE, export );
187         }
188 
189         addMavenInstructions( project, analyzer );
190 
191         analyzer.mergeManifest( analyzer.getJar().getManifest() );
192         analyzer.calcManifest();
193 
194         mergeMavenManifest( project, analyzer );
195 
196         return analyzer;
197     }
198 
199 
200     public static void writeManifest( Manifest manifest, File outputFile ) throws IOException
201     {
202         outputFile.getParentFile().mkdirs();
203 
204         FileOutputStream os;
205         os = new FileOutputStream( outputFile );
206         try
207         {
208             Jar.writeManifest( manifest, os );
209         }
210         finally
211         {
212             try
213             {
214                 os.close();
215             }
216             catch ( IOException e )
217             {
218                 // nothing we can do here
219             }
220         }
221     }
222 }