blob: a622d784bdaec160220bb43ff81dac2272a829c9 [file] [log] [blame]
Stuart McCulloch8798f6d2008-08-06 17:07:33 +00001/*
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 */
19package org.apache.maven.shared.osgi;
20
Stuart McCullochd98b02e2011-06-28 23:20:37 +000021
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000022import java.io.File;
23import java.io.IOException;
24import java.util.Enumeration;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.Map;
28import java.util.Set;
29import java.util.jar.JarFile;
30import java.util.regex.Matcher;
31import java.util.regex.Pattern;
32import java.util.zip.ZipEntry;
33
34import org.apache.maven.artifact.Artifact;
35
36import aQute.lib.osgi.Analyzer;
37
Stuart McCullochd98b02e2011-06-28 23:20:37 +000038
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000039/**
40 * Default implementation of {@link Maven2OsgiConverter}
41 *
42 * @plexus.component
43 *
44 * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
45 * @version $Id: DefaultMaven2OsgiConverter.java 661727 2008-05-30 14:21:49Z bentmann $
46 */
Stuart McCullochd98b02e2011-06-28 23:20:37 +000047public class DefaultMaven2OsgiConverter implements Maven2OsgiConverter
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000048{
49
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000050 private static final String FILE_SEPARATOR = System.getProperty( "file.separator" );
51
Stuart McCullochd98b02e2011-06-28 23:20:37 +000052
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000053 private String getBundleSymbolicName( String groupId, String artifactId )
54 {
55 return groupId + "." + artifactId;
56 }
57
Stuart McCullochd98b02e2011-06-28 23:20:37 +000058
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000059 /**
60 * Get the symbolic name as groupId + "." + artifactId, with the following exceptions
61 * <ul>
62 * <li>if artifact.getFile is not null and the jar contains a OSGi Manifest with
63 * Bundle-SymbolicName property then that value is returned</li>
64 * <li>if groupId has only one section (no dots) and artifact.getFile is not null then the
65 * first package name with classes is returned. eg. commons-logging:commons-logging ->
66 * org.apache.commons.logging</li>
67 * <li>if artifactId is equal to last section of groupId then groupId is returned. eg.
68 * org.apache.maven:maven -> org.apache.maven</li>
69 * <li>if artifactId starts with last section of groupId that portion is removed. eg.
70 * org.apache.maven:maven-core -> org.apache.maven.core</li>
71 * </ul>
72 */
73 public String getBundleSymbolicName( Artifact artifact )
74 {
Stuart McCulloch3fcc7292009-01-29 17:53:38 +000075 if ( ( artifact.getFile() != null ) && artifact.getFile().isFile() )
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000076 {
77 Analyzer analyzer = new Analyzer();
78
Stuart McCulloch3262a0b2008-08-06 17:14:25 +000079 JarFile jar = null;
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000080 try
81 {
Stuart McCulloch3262a0b2008-08-06 17:14:25 +000082 jar = new JarFile( artifact.getFile(), false );
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000083
84 if ( jar.getManifest() != null )
85 {
86 String symbolicNameAttribute = jar.getManifest().getMainAttributes()
87 .getValue( Analyzer.BUNDLE_SYMBOLICNAME );
88 Map bundleSymbolicNameHeader = analyzer.parseHeader( symbolicNameAttribute );
89
90 Iterator it = bundleSymbolicNameHeader.keySet().iterator();
91 if ( it.hasNext() )
92 {
Stuart McCullochd98b02e2011-06-28 23:20:37 +000093 return ( String ) it.next();
Stuart McCulloch8798f6d2008-08-06 17:07:33 +000094 }
95 }
96 }
97 catch ( IOException e )
98 {
99 throw new ManifestReadingException( "Error reading manifest in jar "
100 + artifact.getFile().getAbsolutePath(), e );
101 }
Stuart McCulloch3262a0b2008-08-06 17:14:25 +0000102 finally
103 {
104 if ( jar != null )
105 {
106 try
107 {
108 jar.close();
109 }
110 catch ( IOException e )
111 {
112 }
113 }
114 }
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000115 }
116
117 int i = artifact.getGroupId().lastIndexOf( '.' );
Stuart McCulloch3fcc7292009-01-29 17:53:38 +0000118 if ( ( i < 0 ) && ( artifact.getFile() != null ) && artifact.getFile().isFile() )
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000119 {
120 String groupIdFromPackage = getGroupIdFromPackage( artifact.getFile() );
121 if ( groupIdFromPackage != null )
122 {
123 return groupIdFromPackage;
124 }
125 }
126 String lastSection = artifact.getGroupId().substring( ++i );
127 if ( artifact.getArtifactId().equals( lastSection ) )
128 {
129 return artifact.getGroupId();
130 }
131 if ( artifact.getArtifactId().startsWith( lastSection ) )
132 {
133 String artifactId = artifact.getArtifactId().substring( lastSection.length() );
134 if ( Character.isLetterOrDigit( artifactId.charAt( 0 ) ) )
135 {
136 return getBundleSymbolicName( artifact.getGroupId(), artifactId );
137 }
138 else
139 {
140 return getBundleSymbolicName( artifact.getGroupId(), artifactId.substring( 1 ) );
141 }
142 }
143 return getBundleSymbolicName( artifact.getGroupId(), artifact.getArtifactId() );
144 }
145
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000146
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000147 private String getGroupIdFromPackage( File artifactFile )
148 {
149 try
150 {
151 /* get package names from jar */
152 Set packageNames = new HashSet();
153 JarFile jar = new JarFile( artifactFile, false );
154 Enumeration entries = jar.entries();
155 while ( entries.hasMoreElements() )
156 {
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000157 ZipEntry entry = ( ZipEntry ) entries.nextElement();
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000158 if ( entry.getName().endsWith( ".class" ) )
159 {
160 File f = new File( entry.getName() );
161 String packageName = f.getParent();
162 if ( packageName != null )
163 {
164 packageNames.add( packageName );
165 }
166 }
167 }
Stuart McCulloch3262a0b2008-08-06 17:14:25 +0000168 jar.close();
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000169
170 /* find the top package */
171 String[] groupIdSections = null;
172 for ( Iterator it = packageNames.iterator(); it.hasNext(); )
173 {
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000174 String packageName = ( String ) it.next();
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000175
176 String[] packageNameSections = packageName.split( "\\" + FILE_SEPARATOR );
177 if ( groupIdSections == null )
178 {
179 /* first candidate */
180 groupIdSections = packageNameSections;
181 }
182 else
183 // if ( packageNameSections.length < groupIdSections.length )
184 {
185 /*
186 * find the common portion of current package and previous selected groupId
187 */
188 int i;
189 for ( i = 0; ( i < packageNameSections.length ) && ( i < groupIdSections.length ); i++ )
190 {
191 if ( !packageNameSections[i].equals( groupIdSections[i] ) )
192 {
193 break;
194 }
195 }
196 groupIdSections = new String[i];
197 System.arraycopy( packageNameSections, 0, groupIdSections, 0, i );
198 }
199 }
200
201 if ( ( groupIdSections == null ) || ( groupIdSections.length == 0 ) )
202 {
203 return null;
204 }
205
206 /* only one section as id doesn't seem enough, so ignore it */
207 if ( groupIdSections.length == 1 )
208 {
209 return null;
210 }
211
212 StringBuffer sb = new StringBuffer();
213 for ( int i = 0; i < groupIdSections.length; i++ )
214 {
215 sb.append( groupIdSections[i] );
216 if ( i < groupIdSections.length - 1 )
217 {
218 sb.append( '.' );
219 }
220 }
221 return sb.toString();
222 }
223 catch ( IOException e )
224 {
225 /* we took all the precautions to avoid this */
226 throw new RuntimeException( e );
227 }
228 }
229
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000230
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000231 public String getBundleFileName( Artifact artifact )
232 {
233 return getBundleSymbolicName( artifact ) + "_" + getVersion( artifact.getVersion() ) + ".jar";
234 }
235
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000236
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000237 public String getVersion( Artifact artifact )
238 {
239 return getVersion( artifact.getVersion() );
240 }
241
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000242
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000243 public String getVersion( String version )
244 {
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000245 return cleanupVersion( version );
Guillaume Nodet6da22542010-03-05 14:12:41 +0000246 }
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000247
Guillaume Nodet6da22542010-03-05 14:12:41 +0000248 /**
249 * Clean up version parameters. Other builders use more fuzzy definitions of
250 * the version syntax. This method cleans up such a version to match an OSGi
251 * version.
252 *
253 * @param VERSION_STRING
254 * @return
255 */
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000256 static final Pattern FUZZY_VERSION = Pattern.compile( "(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
257 Pattern.DOTALL );
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000258
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000259
260 static public String cleanupVersion( String version )
261 {
Guillaume Nodet6da22542010-03-05 14:12:41 +0000262 StringBuffer result = new StringBuffer();
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000263 Matcher m = FUZZY_VERSION.matcher( version );
264 if ( m.matches() )
265 {
266 String major = m.group( 1 );
267 String minor = m.group( 3 );
268 String micro = m.group( 5 );
269 String qualifier = m.group( 7 );
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000270
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000271 if ( major != null )
272 {
273 result.append( major );
274 if ( minor != null )
275 {
276 result.append( "." );
277 result.append( minor );
278 if ( micro != null )
279 {
280 result.append( "." );
281 result.append( micro );
282 if ( qualifier != null )
283 {
284 result.append( "." );
285 cleanupModifier( result, qualifier );
Guillaume Nodet6da22542010-03-05 14:12:41 +0000286 }
Guillaume Nodet6da22542010-03-05 14:12:41 +0000287 }
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000288 else if ( qualifier != null )
289 {
290 result.append( ".0." );
291 cleanupModifier( result, qualifier );
292 }
293 else
294 {
295 result.append( ".0" );
296 }
297 }
298 else if ( qualifier != null )
299 {
300 result.append( ".0.0." );
301 cleanupModifier( result, qualifier );
302 }
303 else
304 {
305 result.append( ".0.0" );
Guillaume Nodet6da22542010-03-05 14:12:41 +0000306 }
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000307 }
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000308 }
309 else
310 {
311 result.append( "0.0.0." );
312 cleanupModifier( result, version );
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000313 }
Guillaume Nodet6da22542010-03-05 14:12:41 +0000314 return result.toString();
315 }
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000316
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000317
318 static void cleanupModifier( StringBuffer result, String modifier )
319 {
320 for ( int i = 0; i < modifier.length(); i++ )
321 {
322 char c = modifier.charAt( i );
323 if ( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_'
324 || c == '-' )
325 result.append( c );
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000326 else
Stuart McCullochd98b02e2011-06-28 23:20:37 +0000327 result.append( '_' );
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000328 }
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000329 }
330
Stuart McCulloch8798f6d2008-08-06 17:07:33 +0000331}