1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.felix.bundleplugin;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.util.Formatter;
25 import java.util.Set;
26 import java.util.TreeSet;
27 import java.util.jar.Attributes;
28 import java.util.jar.JarEntry;
29 import java.util.jar.JarInputStream;
30 import java.util.jar.Manifest;
31 import java.util.regex.Pattern;
32
33 import org.apache.felix.utils.manifest.Clause;
34 import org.apache.felix.utils.manifest.Parser;
35 import org.apache.maven.plugin.AbstractMojo;
36 import org.apache.maven.plugin.MojoExecutionException;
37 import org.apache.maven.plugin.MojoFailureException;
38 import org.apache.maven.plugins.annotations.Component;
39 import org.apache.maven.plugins.annotations.LifecyclePhase;
40 import org.apache.maven.plugins.annotations.Mojo;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.project.MavenProject;
43
44
45
46
47
48
49
50
51
52 @Mojo(
53 name = "verify",
54 threadSafe = true,
55 defaultPhase = LifecyclePhase.VERIFY
56 )
57 public final class VerifyBundlePlugin
58 extends AbstractMojo
59 {
60
61 private static final String EXPORT_PACKAGE = "Export-Package";
62
63 private Pattern skipDirs = Pattern.compile( "(META|OSGI)-INF(.*)" );
64
65 @Component
66 private MavenProject project;
67
68
69
70
71 @Parameter( property = "skip", defaultValue = "false" )
72 protected boolean skip;
73
74
75
76
77 @Parameter( property = "failOnError", defaultValue = "true" )
78 protected boolean failOnError;
79
80 @Override
81 public void execute()
82 throws MojoExecutionException, MojoFailureException
83 {
84 if ( skip )
85 {
86 getLog().info( "Skipping Verify execution" );
87 return;
88 }
89
90 Set<String> packagesNotFound = checkPackages();
91
92 if ( !packagesNotFound.isEmpty() )
93 {
94 Formatter formatter = new Formatter();
95 formatter.format( "Current bundle %s exports packages that do not exist:%n",
96 project.getArtifact().getFile() );
97 for ( String packageNotFound : packagesNotFound )
98 {
99 formatter.format( " * %s%n", packageNotFound );
100 }
101 formatter.format( "Please review the <Export-Package> instruction in the `configuration/instructions` element of the `maven-bundle-plugin`%n" );
102 formatter.format( "For more details, see http://bnd.bndtools.org/heads/export_package.html" );
103 String message = formatter.toString();
104 formatter.close();
105
106 if ( failOnError )
107 {
108 throw new MojoFailureException( message );
109 }
110 else
111 {
112 getLog().warn( message );
113 }
114 }
115 }
116
117 private Set<String> checkPackages()
118 throws MojoExecutionException
119 {
120 Set<String> packagesNotFound = new TreeSet<String>();
121
122 File bundle = project.getArtifact().getFile();
123 JarInputStream input = null;
124
125 try
126 {
127 input = new JarInputStream( new FileInputStream( bundle ) );
128 Manifest manifest = input.getManifest();
129 Attributes mainAttributes = manifest.getMainAttributes();
130 String exportPackage = mainAttributes.getValue( EXPORT_PACKAGE );
131
132 if ( exportPackage == null || exportPackage.isEmpty() )
133 {
134 getLog().warn( "Bundle manifest file does not contain valid 'Export-Package' OSGi entry, it will be ignored" );
135 return packagesNotFound;
136 }
137
138
139
140 Clause[] clauses = Parser.parseHeader( exportPackage );
141 for ( Clause clause : clauses )
142 {
143 packagesNotFound.add( clause.getName() );
144 }
145
146
147 JarEntry jarEntry = null;
148 while ( ( jarEntry = input.getNextJarEntry() ) != null )
149 {
150 String entryName = jarEntry.getName();
151 if ( jarEntry.isDirectory() && !skipDirs.matcher( entryName ).matches() )
152 {
153 if ( File.separatorChar == entryName.charAt( entryName.length() - 1 ) )
154 {
155 entryName = entryName.substring( 0, entryName.length() - 1 );
156 }
157
158 String currentPackage = entryName.replace( File.separatorChar, '.' );
159 packagesNotFound.remove( currentPackage );
160 }
161 }
162
163
164 return packagesNotFound;
165 }
166 catch ( IOException ioe )
167 {
168 throw new MojoExecutionException( "An error occurred while reading manifest file " + bundle, ioe );
169 }
170 finally
171 {
172 if ( input != null )
173 {
174 try
175 {
176 input.close();
177 }
178 catch ( IOException e )
179 {
180
181 }
182 }
183 }
184 }
185
186 }