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
22 import java.io.BufferedReader;
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.net.URL;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedHashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.TreeSet;
38 import java.util.regex.Pattern;
39
40 import javax.xml.transform.Transformer;
41 import javax.xml.transform.TransformerFactory;
42 import javax.xml.transform.stream.StreamResult;
43 import javax.xml.transform.stream.StreamSource;
44
45 import aQute.bnd.header.Attrs;
46 import aQute.bnd.service.AnalyzerPlugin;
47 import aQute.bnd.osgi.Analyzer;
48 import aQute.bnd.osgi.Descriptors.PackageRef;
49 import aQute.bnd.osgi.Jar;
50 import aQute.bnd.osgi.Processor;
51 import aQute.bnd.osgi.Resource;
52 import aQute.libg.generics.Create;
53 import aQute.libg.qtokens.QuotedTokenizer;
54 import aQute.service.reporter.Reporter;
55 import org.apache.felix.utils.manifest.Attribute;
56 import org.apache.felix.utils.manifest.Clause;
57 import org.apache.felix.utils.manifest.Directive;
58 import org.osgi.framework.Constants;
59
60 import static org.apache.felix.utils.manifest.Parser.parseHeader;
61
62
63 public class BlueprintPlugin implements AnalyzerPlugin
64 {
65
66 static Pattern QN = Pattern.compile( "[_A-Za-z$][_A-Za-z0-9$]*(\\.[_A-Za-z$][_A-Za-z0-9$]*)*" );
67 static Pattern PATHS = Pattern.compile( ".*\\.xml" );
68
69 Transformer transformer;
70
71
72 public BlueprintPlugin() throws Exception
73 {
74 transformer = getTransformer( getClass().getResource( "blueprint.xsl" ) );
75 }
76
77
78 public boolean analyzeJar( Analyzer analyzer ) throws Exception
79 {
80 String mode = analyzer.getProperty("service_mode");
81 if (mode == null) {
82 mode = "service";
83 }
84
85 transformer.setParameter( "nsh_interface",
86 analyzer.getProperty( "nsh_interface" ) != null ? analyzer.getProperty( "nsh_interface" ) : "" );
87 transformer.setParameter( "nsh_namespace",
88 analyzer.getProperty( "nsh_namespace" ) != null ? analyzer.getProperty( "nsh_namespace" ) : "" );
89
90 Set<String> headers = Create.set();
91
92 String bpHeader = analyzer.getProperty( "Bundle-Blueprint", "OSGI-INF/blueprint" );
93 Map<String, ? extends Map<String, String>> map = Processor.parseHeader( bpHeader, null );
94 bpHeader = "";
95 for ( String root : map.keySet() )
96 {
97 Jar jar = analyzer.getJar();
98 Map<String, Resource> dir = jar.getDirectories().get( root );
99 if ( dir == null || dir.isEmpty() )
100 {
101 Resource resource = jar.getResource( root );
102 if ( resource != null )
103 {
104 process( analyzer, root, resource, headers );
105 if (bpHeader.length() > 0) {
106 bpHeader += ",";
107 }
108 bpHeader += root;
109 }
110 continue;
111 }
112 for ( Map.Entry<String, Resource> entry : dir.entrySet() )
113 {
114 String path = entry.getKey();
115 Resource resource = entry.getValue();
116 if ( PATHS.matcher( path ).matches() )
117 {
118 process( analyzer, path, resource, headers );
119 if (bpHeader.length() > 0) {
120 bpHeader += ",";
121 }
122 bpHeader += path;
123 }
124 }
125 }
126 if( !map.isEmpty() )
127 {
128 analyzer.setProperty("Bundle-Blueprint", bpHeader);
129 }
130
131
132 Set<String> caps = Create.set();
133 Set<String> reqs = Create.set();
134 Map<String, Set<Clause>> hdrs = Create.map();
135 for ( String str : headers )
136 {
137 int idx = str.indexOf( ':' );
138 if ( idx < 0 )
139 {
140 analyzer.warning( ( new StringBuilder( "Error analyzing services in blueprint resource: " ) ).append(
141 str ).toString() );
142 continue;
143 }
144 String h = str.substring( 0, idx ).trim();
145 String v = str.substring( idx + 1 ).trim();
146 Clause[] hc = parseHeader(v);
147
148 if ("Import-Service".equals(h))
149 {
150 if (!"service".equals(mode))
151 {
152 Clause clause = hc[0];
153 String multiple = clause.getDirective("multiple");
154 String avail = clause.getDirective("availability");
155 String filter = clause.getAttribute("filter");
156
157 StringBuilder sb = new StringBuilder();
158 sb.append("osgi.service;effective:=active;");
159 if ("optional".equals(avail)) {
160 sb.append("resolution:=optional;");
161 }
162 if ("true".equals(multiple)) {
163 sb.append("cardinality:=multiple;");
164 }
165 if (filter == null) {
166 filter = "(" + Constants.OBJECTCLASS + "=" + clause.getName() + ")";
167 } else if (!filter.startsWith("(") && !filter.endsWith(")")) {
168 filter = "(&(" + Constants.OBJECTCLASS + "=" + clause.getName() + ")(" + filter + "))";
169 } else {
170 filter = "(&(" + Constants.OBJECTCLASS + "=" + clause.getName() + ")" + filter + ")";
171 }
172 sb.append("filter:=\"").append(filter).append("\"");
173 reqs.add(sb.toString());
174 }
175 else if (!"generic".equals(mode))
176 {
177 Set<Clause> clauses = hdrs.get(h);
178 if (clauses == null) {
179 clauses = new HashSet<Clause>();
180 hdrs.put(h, clauses);
181 }
182 clauses.addAll(Arrays.asList(hc));
183 }
184 }
185 else if ("Export-Service".equals(h))
186 {
187 if (!"service".equals(mode))
188 {
189 StringBuilder sb = new StringBuilder();
190 sb.append("osgi.service;effective:=active;objectClass");
191 if (hc.length > 1) {
192 sb.append(":List<String>=\"");
193 } else {
194 sb.append("=\"");
195 }
196 for (int i = 0; i < hc.length; i++)
197 {
198 if (i > 0)
199 {
200 sb.append(",");
201 }
202 sb.append(hc[i].getName());
203 }
204 sb.append("\"");
205 for (int i = 0; i < hc[0].getAttributes().length; i++)
206 {
207 sb.append(";");
208 sb.append(hc[0].getAttributes()[i].getName());
209 sb.append("=\"");
210 sb.append(hc[0].getAttributes()[i].getValue());
211 sb.append("\"");
212 }
213 caps.add(sb.toString());
214 }
215 else if (!"generic".equals(mode))
216 {
217 Set<Clause> clauses = hdrs.get(h);
218 if (clauses == null) {
219 clauses = new HashSet<Clause>();
220 hdrs.put(h, clauses);
221 }
222 clauses.addAll(Arrays.asList(hc));
223 }
224 }
225 else
226 {
227 Set<Clause> clauses = hdrs.get(h);
228 if (clauses == null)
229 {
230 clauses = new HashSet<Clause>();
231 hdrs.put(h, clauses);
232 }
233 clauses.addAll(Arrays.asList( hc ) );
234 }
235 }
236 if (!caps.isEmpty())
237 {
238 StringBuilder sb = new StringBuilder();
239 String header = analyzer.getProperty("Provide-Capability");
240 if (header != null)
241 {
242 sb.append(header);
243 }
244 for (String cap : caps) {
245 if (sb.length() > 0) {
246 sb.append(",");
247 }
248 sb.append(cap);
249 }
250 analyzer.setProperty("Provide-Capability", sb.toString());
251 }
252 if (!reqs.isEmpty())
253 {
254 StringBuilder sb = new StringBuilder();
255 String header = analyzer.getProperty("Require-Capability");
256 if (header != null)
257 {
258 sb.append(header);
259 }
260 for (String req : reqs) {
261 if (sb.length() > 0) {
262 sb.append(",");
263 }
264 sb.append(req);
265 }
266 analyzer.setProperty("Require-Capability", sb.toString());
267 }
268
269 for ( String header : hdrs.keySet() )
270 {
271 if ( "Import-Class".equals( header ) || "Import-Package".equals( header ) )
272 {
273 Set<Clause> newAttr = hdrs.get(header);
274 for ( Clause a : newAttr )
275 {
276 String pkg = a.getName();
277 if ( "Import-Class".equals( header ) )
278 {
279 int n = a.getName().lastIndexOf( '.' );
280 if ( n > 0 )
281 {
282 pkg = pkg.subSequence( 0, n ).toString();
283 }
284 else
285 {
286 continue;
287 }
288 }
289 PackageRef pkgRef = analyzer.getPackageRef( pkg );
290 if ( !analyzer.getReferred().containsKey( pkgRef ) )
291 {
292 Attrs attrs = analyzer.getReferred().put(pkgRef);
293 for (Attribute attribute : a.getAttributes())
294 {
295 attrs.put(attribute.getName(), attribute.getValue());
296 }
297 }
298 }
299 }
300 else
301 {
302 Set<String> merge = Create.set();
303 String org = analyzer.getProperty(header);
304 if (org != null && !org.isEmpty())
305 {
306 for (Clause clause : parseHeader(org))
307 {
308 merge.add(clause.toString());
309 }
310 }
311 for (Clause clause : hdrs.get(header))
312 {
313 merge.add(clause.toString());
314 }
315 StringBuilder sb = new StringBuilder();
316 for (String clause : merge)
317 {
318 if ( sb.length() > 0 )
319 {
320 sb.append( "," );
321 }
322 sb.append(clause);
323
324 }
325 analyzer.setProperty( header, sb.toString() );
326 }
327 }
328 return false;
329 }
330
331
332 private void process( Analyzer analyzer, String path, Resource resource, Set<String> headers )
333 {
334 InputStream in = null;
335 try
336 {
337 in = resource.openInputStream();
338
339
340 Set<String> set = analyze( in );
341 headers.addAll( set );
342 }
343 catch ( Exception e )
344 {
345 analyzer.error( ( new StringBuilder( "Unexpected exception in processing spring resources(" ) )
346 .append( path ).append( "): " ).append( e ).toString() );
347 }
348 finally
349 {
350 try
351 {
352 if ( in != null )
353 {
354 in.close();
355 }
356 }
357 catch ( IOException e )
358 {
359 }
360 }
361 }
362
363
364 public Set<String> analyze( InputStream in ) throws Exception
365 {
366 Set<String> refers = new HashSet<String>();
367 ByteArrayOutputStream bout = new ByteArrayOutputStream();
368 javax.xml.transform.Result r = new StreamResult( bout );
369 javax.xml.transform.Source s = new StreamSource( in );
370 transformer.transform( s, r );
371 ByteArrayInputStream bin = new ByteArrayInputStream( bout.toByteArray() );
372 bout.close();
373 BufferedReader br = new BufferedReader( new InputStreamReader( bin ) );
374 for ( String line = br.readLine(); line != null; line = br.readLine() )
375 {
376 line = line.trim();
377 line = line.replace( ";availability:=mandatory", "" );
378 if ( line.length() > 0 )
379 {
380 refers.add( line );
381 }
382 }
383
384 br.close();
385 return refers;
386 }
387
388
389 protected Transformer getTransformer( URL url ) throws Exception
390 {
391 TransformerFactory tf = TransformerFactory.newInstance();
392 javax.xml.transform.Source source = new StreamSource( url.openStream() );
393 return tf.newTransformer( source );
394 }
395
396 }