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