View Javadoc

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.maven.plugin.eclipse.writers;
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStreamReader;
28  import java.io.OutputStreamWriter;
29  import java.io.Writer;
30  import java.util.Arrays;
31  
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugin.eclipse.Messages;
34  import org.apache.maven.plugin.ide.IdeDependency;
35  import org.codehaus.plexus.util.IOUtil;
36  import org.codehaus.plexus.util.StringUtils;
37  
38  /**
39   * The <code>EclipseOSGiManifestWriter</code> ensures that value of the "Bundle-Classpath" property in
40   * META-INF/MANIFEST.MF is synchronized with the POM by adding all dependencies that don't have the scope provided.
41   * 
42   * @deprecated use <a href="http://cwiki.apache.org/FELIX/bundle-plugin-for-maven-bnd.html/">Maven Bundle plugin</a>
43   *             from Felix
44   */
45  public class EclipseOSGiManifestWriter
46      extends AbstractEclipseWriter
47  {
48  
49      /**
50       * Constant used for newline.
51       * 
52       * @todo check if we should use system-dependent newlines or if eclipse prefers a common format
53       */
54      private static final String NEWLINE = "\n";
55  
56      /**
57       * Bundle classpath: updated with the list of dependencies.
58       */
59      public final static String ENTRY_BUNDLE_CLASSPATH = "Bundle-ClassPath:";
60  
61      /**
62       * Bundle name: updated with the project name.
63       */
64      public final static String ENTRY_BUNDLE_NAME = "Bundle-Name:";
65  
66      /**
67       * Bundle symbolic name: updated with the artifact id.
68       */
69      public final static String ENTRY_BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName:";
70  
71      /**
72       * Bundle version: updated with the project version.
73       */
74      public final static String ENTRY_BUNDLE_VERSION = "Bundle-Version:";
75  
76      /**
77       * Bundle vendor: updated with the organization name (if set in the POM).
78       */
79      public final static String ENTRY_BUNDLE_VENDOR = "Bundle-Vendor:";
80  
81      /**
82       * @see org.apache.maven.plugin.eclipse.writers.EclipseWriter#write()
83       */
84      public void write()
85          throws MojoExecutionException
86      {
87          // check for existence
88          if ( !config.getOSGIManifestFile().exists() )
89          {
90              log.warn( Messages.getString( "EclipseOSGiManifestWriter.nomanifestfile",
91                                            config.getOSGIManifestFile().getAbsolutePath() ) );
92              return;
93          }
94  
95          StringBuffer manifestSb = rewriteManifest( config.getOSGIManifestFile() );
96          Writer out = null;
97          try
98          {
99              out = new OutputStreamWriter( new FileOutputStream( config.getOSGIManifestFile() ), "UTF-8" );
100             out.write( manifestSb.toString() );
101         }
102         catch ( FileNotFoundException e )
103         {
104             throw new MojoExecutionException( Messages.getString( "cantwritetofile",
105                                                                   config.getOSGIManifestFile().getAbsolutePath() ) );
106         }
107         catch ( IOException e )
108         {
109             throw new MojoExecutionException( Messages.getString( "cantwritetofile",
110                                                                   config.getOSGIManifestFile().getAbsolutePath() ), e );
111         }
112         finally
113         {
114             IOUtil.close( out );
115         }
116     }
117 
118     protected StringBuffer rewriteManifest( File manifestFile )
119         throws MojoExecutionException
120     {
121 
122         // warning: we read and rewrite the file line by line in order to preserve formatting
123         boolean inBundleClasspathEntry = false;
124         StringBuffer manifestSb = new StringBuffer();
125         try
126         {
127             BufferedReader in =
128                 new BufferedReader( new InputStreamReader( new FileInputStream( manifestFile ), "UTF-8" ) );
129             String line;
130             while ( ( line = in.readLine() ) != null )
131             {
132                 if ( inBundleClasspathEntry && line.indexOf( ":" ) > -1 )
133                 {
134                     inBundleClasspathEntry = false;
135                 }
136                 else if ( inBundleClasspathEntry )
137                 {
138                     // skip it
139                     continue;
140                 }
141 
142                 // Note that this could be the empty string, if we encounter
143                 // a field that we weren't expecting to be multi-line.
144                 String name = line.substring( 0, line.indexOf( ":" ) + 1 );
145 
146                 if ( name.equalsIgnoreCase( ENTRY_BUNDLE_CLASSPATH ) )
147                 {
148                     inBundleClasspathEntry = true;
149                 }
150                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_NAME ) )
151                 {
152                     manifestSb.append( ENTRY_BUNDLE_NAME );
153                     manifestSb.append( " " );
154                     manifestSb.append( config.getProject().getName() );
155                     manifestSb.append( NEWLINE );
156                 }
157                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_SYMBOLICNAME ) )
158                 {
159                     manifestSb.append( ENTRY_BUNDLE_SYMBOLICNAME );
160                     manifestSb.append( " " );
161                     manifestSb.append( config.getEclipseProjectName() );
162                     manifestSb.append( ";singleton:=true" );
163                     manifestSb.append( NEWLINE );
164                 }
165                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_VERSION ) )
166                 {
167                     manifestSb.append( ENTRY_BUNDLE_VERSION );
168                     manifestSb.append( " " );
169                     manifestSb.append( getNormalizedVersion( config.getProject().getVersion() ) );
170                     manifestSb.append( NEWLINE );
171                 }
172                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_VENDOR ) && config.getProject().getOrganization() != null )
173                 {
174                     manifestSb.append( ENTRY_BUNDLE_VENDOR );
175                     manifestSb.append( " " );
176                     manifestSb.append( config.getProject().getOrganization().getName() );
177                     manifestSb.append( NEWLINE );
178                 }
179                 else
180                 {
181                     manifestSb.append( line + NEWLINE );
182                 }
183             }
184 
185             IOUtil.close( in );
186         }
187         catch ( IOException e )
188         {
189             throw new MojoExecutionException( Messages.getString( "cantreadfile", manifestFile.getAbsolutePath() ) );
190         }
191         manifestSb.append( addBundleClasspathEntries() );
192 
193         // OSGi manifest headers need to end with a line break
194         manifestSb.append( NEWLINE );
195         return manifestSb;
196     }
197 
198     /**
199      * Normalize a version number, by moving snapshot identifier to the 5th token (first 4 tokens must be numeric for
200      * OSGI bundles)
201      * 
202      * @param version original version
203      * @return a normalized version number
204      */
205     protected static String getNormalizedVersion( String version )
206     {
207 
208         if ( version.endsWith( "-SNAPSHOT" ) )
209         {
210             String[] versionTokens = StringUtils.split( StringUtils.stripEnd( version, "-SNAPSHOT" ), "." );
211 
212             int j = 0;
213             StringBuffer newVersion = new StringBuffer( 20 );
214             for ( ; j < versionTokens.length; j++ )
215             {
216                 newVersion.append( versionTokens[j] );
217                 newVersion.append( "." );
218             }
219             for ( ; j < 3; j++ )
220             {
221                 newVersion.append( "0." );
222             }
223 
224             newVersion.append( "SNAPSHOT" );
225             version = newVersion.toString();
226 
227         }
228         return version;
229     }
230 
231     /**
232      * Add all libraries that don't have the scope "provided" to the "Bundle-Classpath".
233      * 
234      * @return complete "Bundle-ClassPath:" entry for manifest
235      */
236     protected String addBundleClasspathEntries()
237     {
238         StringBuffer bundleClasspathSb = new StringBuffer( ENTRY_BUNDLE_CLASSPATH );
239 
240         // local classes, if the plugin is jarred
241         // @todo handle expanded plugins
242         bundleClasspathSb.append( " ." );
243 
244         IdeDependency[] deps = config.getDepsOrdered();
245 
246         // since Manifest is supposed to be in SVN, having the order of classpath entries shuffled at each run is very
247         // annoying. For now just sort them by using groupId/artifactId
248         Arrays.sort( deps );
249 
250         for ( int j = 0; j < deps.length; j++ )
251         {
252             IdeDependency dep = deps[j];
253             if ( !dep.isProvided() && !dep.isReferencedProject() && !dep.isTestDependency() && !dep.isOsgiBundle() )
254             {
255                 bundleClasspathSb.append( "," + NEWLINE );
256 
257                 log.debug( "Adding artifact to manifest: " + dep.getArtifactId() );
258 
259                 bundleClasspathSb.append( " " + dep.getFile().getName() );
260             }
261         }
262         // only insert the name of the property if there are local libraries
263         return bundleClasspathSb.toString();
264     }
265 
266 }