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  
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.eclipse.Messages;
33  import org.apache.maven.plugin.ide.IdeDependency;
34  import org.codehaus.plexus.util.IOUtil;
35  import org.codehaus.plexus.util.StringUtils;
36  
37  /**
38   * The <code>EclipseOSGiManifestWriter</code> ensures that value of the "Bundle-Classpath" property in
39   * META-INF/MANIFEST.MF is synchronized with the POM by adding all dependencies that don't have the scope provided.
40   * 
41   * @deprecated use <a href="http://cwiki.apache.org/FELIX/bundle-plugin-for-maven-bnd.html/">Maven Bundle plugin</a>
42   *             from Felix
43   */
44  public class EclipseOSGiManifestWriter
45      extends AbstractEclipseWriter
46  {
47  
48      /**
49       * Constant used for newline.
50       * 
51       * @todo check if we should use system-dependent newlines or if eclipse prefers a common format
52       */
53      private static final String NEWLINE = "\n";
54  
55      /**
56       * Bundle classpath: updated with the list of dependencies.
57       */
58      public final static String ENTRY_BUNDLE_CLASSPATH = "Bundle-ClassPath:";
59  
60      /**
61       * Bundle name: updated with the project name.
62       */
63      public final static String ENTRY_BUNDLE_NAME = "Bundle-Name:";
64  
65      /**
66       * Bundle symbolic name: updated with the artifact id.
67       */
68      public final static String ENTRY_BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName:";
69  
70      /**
71       * Bundle version: updated with the project version.
72       */
73      public final static String ENTRY_BUNDLE_VERSION = "Bundle-Version:";
74  
75      /**
76       * Bundle vendor: updated with the organization name (if set in the POM).
77       */
78      public final static String ENTRY_BUNDLE_VENDOR = "Bundle-Vendor:";
79  
80      /**
81       * @see org.apache.maven.plugin.eclipse.writers.EclipseWriter#write()
82       */
83      public void write()
84          throws MojoExecutionException
85      {
86          // check for existence
87          if ( !config.getOSGIManifestFile().exists() )
88          {
89              log.warn( Messages.getString( "EclipseOSGiManifestWriter.nomanifestfile",
90                                            config.getOSGIManifestFile().getAbsolutePath() ) );
91              return;
92          }
93  
94          StringBuffer manifestSb = rewriteManifest( config.getOSGIManifestFile() );
95          Writer out = null;
96          try
97          {
98              out = new OutputStreamWriter( new FileOutputStream( config.getOSGIManifestFile() ), "UTF-8" );
99              out.write( manifestSb.toString() );
100         }
101         catch ( FileNotFoundException e )
102         {
103             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantwritetofile",
104                                                                   config.getOSGIManifestFile().getAbsolutePath() ) );
105         }
106         catch ( IOException e )
107         {
108             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantwritetofile",
109                                                                   config.getOSGIManifestFile().getAbsolutePath() ), e );
110         }
111         finally
112         {
113             IOUtil.close( out );
114         }
115     }
116 
117     protected StringBuffer rewriteManifest( File manifestFile )
118         throws MojoExecutionException
119     {
120 
121         // warning: we read and rewrite the file line by line in order to preserve formatting
122         boolean inBundleClasspathEntry = false;
123         StringBuffer manifestSb = new StringBuffer();
124         try
125         {
126             BufferedReader in =
127                 new BufferedReader( new InputStreamReader( new FileInputStream( manifestFile ), "UTF-8" ) );
128             String line;
129             while ( ( line = in.readLine() ) != null )
130             {
131                 if ( inBundleClasspathEntry && line.indexOf( ":" ) > -1 )
132                 {
133                     inBundleClasspathEntry = false;
134                 }
135                 else if ( inBundleClasspathEntry )
136                 {
137                     // skip it
138                     continue;
139                 }
140 
141                 // Note that this could be the empty string, if we encounter
142                 // a field that we weren't expecting to be multi-line.
143                 String name = line.substring( 0, line.indexOf( ":" ) + 1 );
144 
145                 if ( name.equalsIgnoreCase( ENTRY_BUNDLE_CLASSPATH ) )
146                 {
147                     inBundleClasspathEntry = true;
148                 }
149                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_NAME ) )
150                 {
151                     manifestSb.append( ENTRY_BUNDLE_NAME );
152                     manifestSb.append( " " );
153                     manifestSb.append( config.getProject().getName() );
154                     manifestSb.append( NEWLINE );
155                 }
156                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_SYMBOLICNAME ) )
157                 {
158                     manifestSb.append( ENTRY_BUNDLE_SYMBOLICNAME );
159                     manifestSb.append( " " );
160                     manifestSb.append( config.getEclipseProjectName() );
161                     manifestSb.append( ";singleton:=true" );
162                     manifestSb.append( NEWLINE );
163                 }
164                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_VERSION ) )
165                 {
166                     manifestSb.append( ENTRY_BUNDLE_VERSION );
167                     manifestSb.append( " " );
168                     manifestSb.append( getNormalizedVersion( config.getProject().getVersion() ) );
169                     manifestSb.append( NEWLINE );
170                 }
171                 else if ( name.equalsIgnoreCase( ENTRY_BUNDLE_VENDOR ) && config.getProject().getOrganization() != null )
172                 {
173                     manifestSb.append( ENTRY_BUNDLE_VENDOR );
174                     manifestSb.append( " " );
175                     manifestSb.append( config.getProject().getOrganization().getName() );
176                     manifestSb.append( NEWLINE );
177                 }
178                 else
179                 {
180                     manifestSb.append( line + NEWLINE );
181                 }
182             }
183 
184             IOUtil.close( in );
185         }
186         catch ( IOException e )
187         {
188             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantreadfile",
189                                                                   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.getDeps();
245 
246         for ( int j = 0; j < deps.length; j++ )
247         {
248             IdeDependency dep = deps[j];
249             if ( !dep.isProvided() && !dep.isReferencedProject() && !dep.isTestDependency() && !dep.isOsgiBundle() )
250             {
251                 bundleClasspathSb.append( "," + NEWLINE );
252 
253                 log.debug( "Adding artifact to manifest: " + dep.getArtifactId() );
254 
255                 bundleClasspathSb.append( " " + dep.getFile().getName() );
256             }
257         }
258         // only insert the name of the property if there are local libraries
259         return bundleClasspathSb.toString();
260     }
261 
262 }