1 | /* |
2 | * Copyright (c) 2009 The JOMC Project |
3 | * Copyright (c) 2005 Christian Schulte <cs@jomc.org> |
4 | * All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * |
10 | * o Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * |
13 | * o Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in |
15 | * the documentation and/or other materials provided with the |
16 | * distribution. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS" |
19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
20 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
21 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR |
22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | * |
30 | * $Id: JavaClasses.java 1237 2010-01-09 20:22:54Z schulte2005 $ |
31 | * |
32 | */ |
33 | package org.jomc.tools; |
34 | |
35 | import java.io.ByteArrayInputStream; |
36 | import java.io.ByteArrayOutputStream; |
37 | import java.io.File; |
38 | import java.io.IOException; |
39 | import java.io.InputStream; |
40 | import java.net.URL; |
41 | import java.text.MessageFormat; |
42 | import java.util.List; |
43 | import java.util.ResourceBundle; |
44 | import java.util.logging.Level; |
45 | import java.util.zip.GZIPInputStream; |
46 | import java.util.zip.GZIPOutputStream; |
47 | import javax.xml.bind.JAXBElement; |
48 | import javax.xml.bind.JAXBException; |
49 | import javax.xml.bind.Marshaller; |
50 | import javax.xml.bind.Unmarshaller; |
51 | import javax.xml.bind.util.JAXBResult; |
52 | import javax.xml.bind.util.JAXBSource; |
53 | import javax.xml.transform.Transformer; |
54 | import javax.xml.transform.TransformerException; |
55 | import org.apache.bcel.classfile.Attribute; |
56 | import org.apache.bcel.classfile.ClassParser; |
57 | import org.apache.bcel.classfile.Constant; |
58 | import org.apache.bcel.classfile.ConstantPool; |
59 | import org.apache.bcel.classfile.ConstantUtf8; |
60 | import org.apache.bcel.classfile.JavaClass; |
61 | import org.apache.bcel.classfile.Unknown; |
62 | import org.jomc.model.Dependencies; |
63 | import org.jomc.model.Dependency; |
64 | import org.jomc.model.Implementation; |
65 | import org.jomc.model.Message; |
66 | import org.jomc.model.Messages; |
67 | import org.jomc.model.ModelObject; |
68 | import org.jomc.model.ModelValidationReport; |
69 | import org.jomc.model.Module; |
70 | import org.jomc.model.ObjectFactory; |
71 | import org.jomc.model.Properties; |
72 | import org.jomc.model.Property; |
73 | import org.jomc.model.Specification; |
74 | import org.jomc.model.SpecificationReference; |
75 | import org.jomc.model.Specifications; |
76 | import org.jomc.util.ParseException; |
77 | import org.jomc.util.TokenMgrError; |
78 | import org.jomc.util.VersionParser; |
79 | |
80 | /** |
81 | * Manages Java classes. |
82 | * |
83 | * <p><b>Use cases</b><br/><ul> |
84 | * <li>{@link #commitClasses(javax.xml.bind.Marshaller, java.io.File) }</li> |
85 | * <li>{@link #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File) }</li> |
86 | * <li>{@link #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File) }</li> |
87 | * <li>{@link #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File) }</li> |
88 | * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.io.File) }</li> |
89 | * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li> |
90 | * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) }</li> |
91 | * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li> |
92 | * <li>{@link #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li> |
93 | * <li>{@link #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li> |
94 | * <li>{@link #transformClasses(javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li> |
95 | * <li>{@link #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li> |
96 | * <li>{@link #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li> |
97 | * <li>{@link #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li> |
98 | * </ul></p> |
99 | * |
100 | * @author <a href="mailto:cs@jomc.org">Christian Schulte</a> |
101 | * @version $Id: JavaClasses.java 1237 2010-01-09 20:22:54Z schulte2005 $ |
102 | * |
103 | * @see #getModules() |
104 | */ |
105 | public class JavaClasses extends JomcTool |
106 | { |
107 | |
108 | /** Creates a new {@code JavaClasses} instance. */ |
109 | public JavaClasses() |
110 | { |
111 | super(); |
112 | } |
113 | |
114 | /** |
115 | * Creates a new {@code JavaClasses} instance taking a {@code JavaClasses} instance to initialize the instance with. |
116 | * |
117 | * @param tool The instance to initialize the new instance with, |
118 | */ |
119 | public JavaClasses( final JavaClasses tool ) |
120 | { |
121 | super( tool ); |
122 | } |
123 | |
124 | /** |
125 | * Commits meta-data of the modules of the instance to compiled Java classes. |
126 | * |
127 | * @param marshaller The marshaller to use for committing the classes. |
128 | * @param classesDirectory The directory holding the compiled class files. |
129 | * |
130 | * @throws NullPointerException if {@code marshaller} or {@code classesDirectory} is {@code null}. |
131 | * @throws ToolException if committing meta-data fails. |
132 | * |
133 | * @see #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File) |
134 | */ |
135 | public void commitClasses( final Marshaller marshaller, final File classesDirectory ) throws ToolException |
136 | { |
137 | if ( marshaller == null ) |
138 | { |
139 | throw new NullPointerException( "marshaller" ); |
140 | } |
141 | if ( classesDirectory == null ) |
142 | { |
143 | throw new NullPointerException( "classesDirectory" ); |
144 | } |
145 | |
146 | for ( Module m : this.getModules().getModule() ) |
147 | { |
148 | this.commitClasses( m, marshaller, classesDirectory ); |
149 | } |
150 | } |
151 | |
152 | /** |
153 | * Commits meta-data of a given module of the modules of the instance to compiled Java classes. |
154 | * |
155 | * @param module The module to process. |
156 | * @param marshaller The marshaller to use for committing the classes. |
157 | * @param classesDirectory The directory holding the compiled class files. |
158 | * |
159 | * @throws NullPointerException if {@code module}, {@code marshaller} or {@code classesDirectory} is {@code null}. |
160 | * @throws ToolException if committing meta-data fails. |
161 | * |
162 | * @see #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File) |
163 | * @see #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File) |
164 | */ |
165 | public void commitClasses( final Module module, final Marshaller marshaller, final File classesDirectory ) |
166 | throws ToolException |
167 | { |
168 | if ( module == null ) |
169 | { |
170 | throw new NullPointerException( "module" ); |
171 | } |
172 | if ( marshaller == null ) |
173 | { |
174 | throw new NullPointerException( "marshaller" ); |
175 | } |
176 | if ( classesDirectory == null ) |
177 | { |
178 | throw new NullPointerException( "classesDirectory" ); |
179 | } |
180 | |
181 | if ( module.getSpecifications() != null ) |
182 | { |
183 | for ( Specification s : module.getSpecifications().getSpecification() ) |
184 | { |
185 | this.commitClasses( s, marshaller, classesDirectory ); |
186 | } |
187 | } |
188 | if ( module.getImplementations() != null ) |
189 | { |
190 | for ( Implementation i : module.getImplementations().getImplementation() ) |
191 | { |
192 | this.commitClasses( i, marshaller, classesDirectory ); |
193 | } |
194 | } |
195 | } |
196 | |
197 | /** |
198 | * Commits meta-data of a given specification of the modules of the instance to compiled Java classes. |
199 | * |
200 | * @param specification The specification to process. |
201 | * @param marshaller The marshaller to use for committing the classes. |
202 | * @param classesDirectory The directory holding the compiled class files. |
203 | * |
204 | * @throws NullPointerException if {@code specification}, {@code marshaller} or {@code classesDirectory} is |
205 | * {@code null}. |
206 | * @throws ToolException if committing meta-data fails. |
207 | * |
208 | * @see org.jomc.model.ModelContext#createMarshaller() |
209 | */ |
210 | public void commitClasses( final Specification specification, final Marshaller marshaller, |
211 | final File classesDirectory ) throws ToolException |
212 | { |
213 | if ( specification == null ) |
214 | { |
215 | throw new NullPointerException( "specification" ); |
216 | } |
217 | if ( marshaller == null ) |
218 | { |
219 | throw new NullPointerException( "marshaller" ); |
220 | } |
221 | if ( classesDirectory == null ) |
222 | { |
223 | throw new NullPointerException( "classesDirectory" ); |
224 | } |
225 | |
226 | try |
227 | { |
228 | if ( specification.isClassDeclaration() ) |
229 | { |
230 | final String classLocation = specification.getClazz().replace( '.', File.separatorChar ) + ".class"; |
231 | final File classFile = new File( classesDirectory, classLocation ); |
232 | if ( this.isLoggable( Level.INFO ) ) |
233 | { |
234 | this.log( Level.INFO, this.getMessage( "committing", new Object[] |
235 | { |
236 | classFile.getAbsolutePath() |
237 | } ), null ); |
238 | |
239 | } |
240 | |
241 | final JavaClass javaClass = this.getJavaClass( classFile ); |
242 | this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject( |
243 | marshaller, new ObjectFactory().createSpecification( specification ) ) ); |
244 | |
245 | javaClass.dump( classFile ); |
246 | } |
247 | } |
248 | catch ( final IOException e ) |
249 | { |
250 | throw new ToolException( e ); |
251 | } |
252 | } |
253 | |
254 | /** |
255 | * Commits meta-data of a given implementation of the modules of the instance to compiled Java classes. |
256 | * |
257 | * @param implementation The implementation to process. |
258 | * @param marshaller The marshaller to use for committing the classes. |
259 | * @param classesDirectory The directory holding the compiled class files. |
260 | * |
261 | * @throws NullPointerException if {@code implementation}, {@code marshaller} or {@code classesDirectory} is |
262 | * {@code null}. |
263 | * @throws ToolException if committing meta-data fails. |
264 | * |
265 | * @see org.jomc.model.ModelContext#createMarshaller() |
266 | */ |
267 | public void commitClasses( final Implementation implementation, final Marshaller marshaller, |
268 | final File classesDirectory ) throws ToolException |
269 | { |
270 | if ( implementation == null ) |
271 | { |
272 | throw new NullPointerException( "implementation" ); |
273 | } |
274 | if ( marshaller == null ) |
275 | { |
276 | throw new NullPointerException( "marshaller" ); |
277 | } |
278 | if ( classesDirectory == null ) |
279 | { |
280 | throw new NullPointerException( "classesDirectory" ); |
281 | } |
282 | |
283 | try |
284 | { |
285 | if ( implementation.isClassDeclaration() ) |
286 | { |
287 | Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() ); |
288 | if ( dependencies == null ) |
289 | { |
290 | dependencies = new Dependencies(); |
291 | } |
292 | |
293 | Properties properties = this.getModules().getProperties( implementation.getIdentifier() ); |
294 | if ( properties == null ) |
295 | { |
296 | properties = new Properties(); |
297 | } |
298 | |
299 | Messages messages = this.getModules().getMessages( implementation.getIdentifier() ); |
300 | if ( messages == null ) |
301 | { |
302 | messages = new Messages(); |
303 | } |
304 | |
305 | Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() ); |
306 | if ( specifications == null ) |
307 | { |
308 | specifications = new Specifications(); |
309 | } |
310 | |
311 | for ( SpecificationReference r : specifications.getReference() ) |
312 | { |
313 | if ( specifications.getSpecification( r.getIdentifier() ) == null && |
314 | this.isLoggable( Level.WARNING ) ) |
315 | { |
316 | this.log( Level.WARNING, this.getMessage( "unresolvedSpecification", new Object[] |
317 | { |
318 | r.getIdentifier(), implementation.getIdentifier() |
319 | } ), null ); |
320 | |
321 | } |
322 | } |
323 | |
324 | for ( Dependency d : dependencies.getDependency() ) |
325 | { |
326 | final Specification s = this.getModules().getSpecification( d.getIdentifier() ); |
327 | |
328 | if ( s != null ) |
329 | { |
330 | if ( specifications.getSpecification( s.getIdentifier() ) == null ) |
331 | { |
332 | specifications.getSpecification().add( s ); |
333 | } |
334 | } |
335 | else if ( this.isLoggable( Level.WARNING ) ) |
336 | { |
337 | this.log( Level.WARNING, this.getMessage( "unresolvedDependencySpecification", new Object[] |
338 | { |
339 | d.getIdentifier(), d.getName(), implementation.getIdentifier() |
340 | } ), null ); |
341 | |
342 | } |
343 | } |
344 | |
345 | final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class"; |
346 | final File classFile = new File( classesDirectory, classLocation ); |
347 | |
348 | if ( this.isLoggable( Level.INFO ) ) |
349 | { |
350 | this.log( Level.INFO, this.getMessage( "committing", new Object[] |
351 | { |
352 | classFile.getAbsolutePath() |
353 | } ), null ); |
354 | |
355 | } |
356 | |
357 | final JavaClass javaClass = this.getJavaClass( classFile ); |
358 | final ObjectFactory of = new ObjectFactory(); |
359 | |
360 | this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject( |
361 | marshaller, of.createDependencies( dependencies ) ) ); |
362 | |
363 | this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject( |
364 | marshaller, of.createProperties( properties ) ) ); |
365 | |
366 | this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject( |
367 | marshaller, of.createMessages( messages ) ) ); |
368 | |
369 | this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject( |
370 | marshaller, of.createSpecifications( specifications ) ) ); |
371 | |
372 | javaClass.dump( classFile ); |
373 | } |
374 | } |
375 | catch ( final IOException e ) |
376 | { |
377 | throw new ToolException( e ); |
378 | } |
379 | } |
380 | |
381 | /** |
382 | * Validates compiled Java classes against the modules of the instance. |
383 | * |
384 | * @param unmarshaller The unmarshaller to use for validating classes. |
385 | * @param classesDirectory The directory holding the compiled class files. |
386 | * |
387 | * @return The report of the validation. |
388 | * |
389 | * @throws NullPointerException if {@code unmarshaller} or {@code classesDirectory} is {@code null}. |
390 | * @throws ToolException if reading class files fails. |
391 | * |
392 | * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) |
393 | */ |
394 | public ModelValidationReport validateClasses( final Unmarshaller unmarshaller, final File classesDirectory ) |
395 | throws ToolException |
396 | { |
397 | if ( unmarshaller == null ) |
398 | { |
399 | throw new NullPointerException( "unmarshaller" ); |
400 | } |
401 | if ( classesDirectory == null ) |
402 | { |
403 | throw new NullPointerException( "classesDirectory" ); |
404 | } |
405 | |
406 | final ModelValidationReport report = new ModelValidationReport(); |
407 | |
408 | for ( Module m : this.getModules().getModule() ) |
409 | { |
410 | final ModelValidationReport current = this.validateClasses( m, unmarshaller, classesDirectory ); |
411 | report.getDetails().addAll( current.getDetails() ); |
412 | } |
413 | |
414 | return report; |
415 | } |
416 | |
417 | /** |
418 | * Validates compiled Java classes against the modules of the instance. |
419 | * |
420 | * @param unmarshaller The unmarshaller to use for validating classes. |
421 | * @param classLoader The class loader to search for classes. |
422 | * |
423 | * @return The report of the validation. |
424 | * |
425 | * @throws NullPointerException if {@code unmarshaller} or {@code classLoader} is {@code null}. |
426 | * @throws ToolException if reading class files fails. |
427 | * |
428 | * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) |
429 | */ |
430 | public ModelValidationReport validateClasses( final Unmarshaller unmarshaller, final ClassLoader classLoader ) |
431 | throws ToolException |
432 | { |
433 | if ( unmarshaller == null ) |
434 | { |
435 | throw new NullPointerException( "unmarshaller" ); |
436 | } |
437 | if ( classLoader == null ) |
438 | { |
439 | throw new NullPointerException( "classLoader" ); |
440 | } |
441 | |
442 | final ModelValidationReport report = new ModelValidationReport(); |
443 | |
444 | for ( Module m : this.getModules().getModule() ) |
445 | { |
446 | final ModelValidationReport current = this.validateClasses( m, unmarshaller, classLoader ); |
447 | report.getDetails().addAll( current.getDetails() ); |
448 | } |
449 | |
450 | return report; |
451 | } |
452 | |
453 | /** |
454 | * Validates compiled Java classes against a given module of the modules of the instance. |
455 | * |
456 | * @param module The module to process. |
457 | * @param unmarshaller The unmarshaller to use for validating classes. |
458 | * @param classesDirectory The directory holding the compiled class files. |
459 | * |
460 | * @return The report of the validation. |
461 | * |
462 | * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classesDirectory} is {@code null}. |
463 | * @throws ToolException if reading class files fails. |
464 | * |
465 | * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
466 | * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
467 | */ |
468 | public ModelValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller, |
469 | final File classesDirectory ) throws ToolException |
470 | { |
471 | if ( module == null ) |
472 | { |
473 | throw new NullPointerException( "module" ); |
474 | } |
475 | if ( unmarshaller == null ) |
476 | { |
477 | throw new NullPointerException( "unmarshaller" ); |
478 | } |
479 | if ( classesDirectory == null ) |
480 | { |
481 | throw new NullPointerException( "classesDirectory" ); |
482 | } |
483 | |
484 | final ModelValidationReport report = new ModelValidationReport(); |
485 | |
486 | if ( module.getSpecifications() != null ) |
487 | { |
488 | for ( Specification s : module.getSpecifications().getSpecification() ) |
489 | { |
490 | if ( s.isClassDeclaration() ) |
491 | { |
492 | final String classLocation = s.getClazz().replace( '.', File.separatorChar ) + ".class"; |
493 | final File classFile = new File( classesDirectory, classLocation ); |
494 | final ModelValidationReport current = |
495 | this.validateClasses( s, unmarshaller, this.getJavaClass( classFile ) ); |
496 | |
497 | report.getDetails().addAll( current.getDetails() ); |
498 | } |
499 | } |
500 | } |
501 | |
502 | if ( module.getImplementations() != null ) |
503 | { |
504 | for ( Implementation i : module.getImplementations().getImplementation() ) |
505 | { |
506 | if ( i.isClassDeclaration() ) |
507 | { |
508 | final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class"; |
509 | final File classFile = new File( classesDirectory, classLocation ); |
510 | final JavaClass javaClass = this.getJavaClass( classFile ); |
511 | final ModelValidationReport current = |
512 | this.validateClasses( i, unmarshaller, javaClass ); |
513 | |
514 | report.getDetails().addAll( current.getDetails() ); |
515 | } |
516 | } |
517 | } |
518 | |
519 | return report; |
520 | } |
521 | |
522 | /** |
523 | * Validates compiled Java classes against a given module of the modules of the instance. |
524 | * |
525 | * @param module The module to process. |
526 | * @param unmarshaller The unmarshaller to use for validating classes. |
527 | * @param classLoader The class loader to search for classes. |
528 | * |
529 | * @return The report of the validation. |
530 | * |
531 | * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classLoader} is {@code null}. |
532 | * @throws ToolException if reading class files fails. |
533 | * |
534 | * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
535 | * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
536 | */ |
537 | public ModelValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller, |
538 | final ClassLoader classLoader ) throws ToolException |
539 | { |
540 | if ( module == null ) |
541 | { |
542 | throw new NullPointerException( "module" ); |
543 | } |
544 | if ( unmarshaller == null ) |
545 | { |
546 | throw new NullPointerException( "unmarshaller" ); |
547 | } |
548 | if ( classLoader == null ) |
549 | { |
550 | throw new NullPointerException( "classLoader" ); |
551 | } |
552 | |
553 | final ModelValidationReport report = new ModelValidationReport(); |
554 | |
555 | if ( module.getSpecifications() != null ) |
556 | { |
557 | for ( Specification s : module.getSpecifications().getSpecification() ) |
558 | { |
559 | if ( s.isClassDeclaration() ) |
560 | { |
561 | final String classLocation = s.getClazz().replace( '.', '/' ) + ".class"; |
562 | final URL classUrl = classLoader.getResource( classLocation ); |
563 | |
564 | if ( classUrl == null ) |
565 | { |
566 | throw new ToolException( this.getMessage( "resourceNotFound", new Object[] |
567 | { |
568 | classLocation |
569 | } ) ); |
570 | |
571 | } |
572 | |
573 | final JavaClass javaClass = this.getJavaClass( classUrl, classLocation ); |
574 | final ModelValidationReport current = |
575 | this.validateClasses( s, unmarshaller, javaClass ); |
576 | |
577 | report.getDetails().addAll( current.getDetails() ); |
578 | } |
579 | } |
580 | } |
581 | |
582 | if ( module.getImplementations() != null ) |
583 | { |
584 | for ( Implementation i : module.getImplementations().getImplementation() ) |
585 | { |
586 | if ( i.isClassDeclaration() ) |
587 | { |
588 | final String classLocation = i.getClazz().replace( '.', '/' ) + ".class"; |
589 | final URL classUrl = classLoader.getResource( classLocation ); |
590 | |
591 | if ( classUrl == null ) |
592 | { |
593 | throw new ToolException( this.getMessage( "resourceNotFound", new Object[] |
594 | { |
595 | classLocation |
596 | } ) ); |
597 | |
598 | } |
599 | |
600 | final JavaClass javaClass = this.getJavaClass( classUrl, classLocation ); |
601 | final ModelValidationReport current = |
602 | this.validateClasses( i, unmarshaller, javaClass ); |
603 | |
604 | report.getDetails().addAll( current.getDetails() ); |
605 | } |
606 | } |
607 | } |
608 | |
609 | return report; |
610 | } |
611 | |
612 | /** |
613 | * Validates compiled Java classes against a given specification of the modules of the instance. |
614 | * |
615 | * @param specification The specification to process. |
616 | * @param unmarshaller The unmarshaller to use for validating classes. |
617 | * @param javaClass The class to validate. |
618 | * |
619 | * @return The report of the validation. |
620 | * |
621 | * @throws NullPointerException if {@code specification}, {@code unmarshaller} or {@code javaClass} is {@code null}. |
622 | * @throws ToolException if reading class files fails. |
623 | * |
624 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
625 | */ |
626 | public ModelValidationReport validateClasses( final Specification specification, |
627 | final Unmarshaller unmarshaller, final JavaClass javaClass ) |
628 | throws ToolException |
629 | { |
630 | if ( specification == null ) |
631 | { |
632 | throw new NullPointerException( "specification" ); |
633 | } |
634 | if ( unmarshaller == null ) |
635 | { |
636 | throw new NullPointerException( "unmarshaller" ); |
637 | } |
638 | if ( javaClass == null ) |
639 | { |
640 | throw new NullPointerException( "javaClass" ); |
641 | } |
642 | |
643 | if ( this.isLoggable( Level.INFO ) ) |
644 | { |
645 | this.log( Level.INFO, this.getMessage( "validatingSpecification", new Object[] |
646 | { |
647 | specification.getIdentifier() |
648 | } ), null ); |
649 | |
650 | } |
651 | |
652 | final ModelValidationReport report = new ModelValidationReport(); |
653 | |
654 | Specification decoded = null; |
655 | final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() ); |
656 | if ( bytes != null ) |
657 | { |
658 | decoded = this.decodeModelObject( unmarshaller, bytes, Specification.class ); |
659 | } |
660 | |
661 | if ( decoded != null ) |
662 | { |
663 | if ( decoded.getMultiplicity() != specification.getMultiplicity() ) |
664 | { |
665 | report.getDetails().add( new ModelValidationReport.Detail( |
666 | "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, |
667 | this.getMessage( "illegalMultiplicity", new Object[] |
668 | { |
669 | specification.getIdentifier(), specification.getMultiplicity().value(), |
670 | decoded.getMultiplicity().value() |
671 | } ), new ObjectFactory().createSpecification( specification ) ) ); |
672 | |
673 | } |
674 | |
675 | if ( decoded.getScope() == null |
676 | ? specification.getScope() != null |
677 | : !decoded.getScope().equals( specification.getScope() ) ) |
678 | { |
679 | report.getDetails().add( new ModelValidationReport.Detail( |
680 | "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, |
681 | this.getMessage( "illegalScope", new Object[] |
682 | { |
683 | specification.getIdentifier(), |
684 | specification.getScope() == null ? "Multiton" : specification.getScope(), |
685 | decoded.getScope() == null ? "Multiton" : decoded.getScope() |
686 | } ), new ObjectFactory().createSpecification( specification ) ) ); |
687 | |
688 | } |
689 | |
690 | if ( decoded.getClazz() == null |
691 | ? specification.getClazz() != null |
692 | : !decoded.getClazz().equals( specification.getClazz() ) ) |
693 | { |
694 | report.getDetails().add( new ModelValidationReport.Detail( |
695 | "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, |
696 | this.getMessage( "illegalSpecificationClass", new Object[] |
697 | { |
698 | decoded.getIdentifier(), specification.getClazz(), decoded.getClazz() |
699 | } ), new ObjectFactory().createSpecification( specification ) ) ); |
700 | |
701 | } |
702 | } |
703 | else if ( this.isLoggable( Level.WARNING ) ) |
704 | { |
705 | this.log( Level.WARNING, this.getMessage( "cannotValidateSpecification", new Object[] |
706 | { |
707 | specification.getIdentifier(), Specification.class.getName() |
708 | } ), null ); |
709 | |
710 | } |
711 | |
712 | return report; |
713 | } |
714 | |
715 | /** |
716 | * Validates compiled Java classes against a given implementation of the modules of the instance. |
717 | * |
718 | * @param implementation The implementation to process. |
719 | * @param unmarshaller The unmarshaller to use for validating classes. |
720 | * @param javaClass The class to validate. |
721 | * |
722 | * @return The report of the validation. |
723 | * |
724 | * @throws NullPointerException if {@code implementation}, {@code unmarshaller} or {@code javaClass} is |
725 | * {@code null}. |
726 | * @throws ToolException if reading class files fails. |
727 | * |
728 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
729 | */ |
730 | public ModelValidationReport validateClasses( final Implementation implementation, |
731 | final Unmarshaller unmarshaller, final JavaClass javaClass ) |
732 | throws ToolException |
733 | { |
734 | if ( implementation == null ) |
735 | { |
736 | throw new NullPointerException( "implementation" ); |
737 | } |
738 | if ( unmarshaller == null ) |
739 | { |
740 | throw new NullPointerException( "unmarshaller" ); |
741 | } |
742 | if ( javaClass == null ) |
743 | { |
744 | throw new NullPointerException( "javaClass" ); |
745 | } |
746 | |
747 | if ( this.isLoggable( Level.INFO ) ) |
748 | { |
749 | this.log( Level.INFO, this.getMessage( "validatingImplementation", new Object[] |
750 | { |
751 | implementation.getIdentifier() |
752 | } ), null ); |
753 | |
754 | } |
755 | |
756 | final ModelValidationReport report = new ModelValidationReport(); |
757 | |
758 | try |
759 | { |
760 | Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() ); |
761 | if ( dependencies == null ) |
762 | { |
763 | dependencies = new Dependencies(); |
764 | } |
765 | |
766 | Properties properties = this.getModules().getProperties( implementation.getIdentifier() ); |
767 | if ( properties == null ) |
768 | { |
769 | properties = new Properties(); |
770 | } |
771 | |
772 | Messages messages = this.getModules().getMessages( implementation.getIdentifier() ); |
773 | if ( messages == null ) |
774 | { |
775 | messages = new Messages(); |
776 | } |
777 | |
778 | Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() ); |
779 | if ( specifications == null ) |
780 | { |
781 | specifications = new Specifications(); |
782 | } |
783 | |
784 | Dependencies decodedDependencies = null; |
785 | byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() ); |
786 | if ( bytes != null ) |
787 | { |
788 | decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class ); |
789 | } |
790 | |
791 | Properties decodedProperties = null; |
792 | bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() ); |
793 | if ( bytes != null ) |
794 | { |
795 | decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class ); |
796 | } |
797 | |
798 | Messages decodedMessages = null; |
799 | bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() ); |
800 | if ( bytes != null ) |
801 | { |
802 | decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class ); |
803 | } |
804 | |
805 | Specifications decodedSpecifications = null; |
806 | bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() ); |
807 | if ( bytes != null ) |
808 | { |
809 | decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class ); |
810 | } |
811 | |
812 | if ( decodedDependencies != null ) |
813 | { |
814 | for ( Dependency decodedDependency : decodedDependencies.getDependency() ) |
815 | { |
816 | final Dependency dependency = dependencies.getDependency( decodedDependency.getName() ); |
817 | |
818 | if ( dependency == null ) |
819 | { |
820 | report.getDetails().add( new ModelValidationReport.Detail( |
821 | "CLASS_MISSING_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, |
822 | this.getMessage( "missingDependency", new Object[] |
823 | { |
824 | implementation.getIdentifier(), decodedDependency.getName() |
825 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
826 | |
827 | } |
828 | |
829 | final Specification s = this.getModules().getSpecification( decodedDependency.getIdentifier() ); |
830 | |
831 | if ( s != null && s.getVersion() != null && decodedDependency.getVersion() != null && |
832 | VersionParser.compare( decodedDependency.getVersion(), s.getVersion() ) > 0 ) |
833 | { |
834 | final Module moduleOfSpecification = |
835 | this.getModules().getModuleOfSpecification( s.getIdentifier() ); |
836 | |
837 | final Module moduleOfImplementation = |
838 | this.getModules().getModuleOfImplementation( implementation.getIdentifier() ); |
839 | |
840 | report.getDetails().add( new ModelValidationReport.Detail( |
841 | "CLASS_INCOMPATIBLE_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, |
842 | this.getMessage( "incompatibleDependency", new Object[] |
843 | { |
844 | javaClass.getClassName(), moduleOfImplementation == null |
845 | ? "<>" : moduleOfImplementation.getName(), |
846 | s.getIdentifier(), moduleOfSpecification == null |
847 | ? "<>" : moduleOfSpecification.getName(), |
848 | decodedDependency.getVersion(), s.getVersion() |
849 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
850 | |
851 | } |
852 | } |
853 | } |
854 | else if ( this.isLoggable( Level.WARNING ) ) |
855 | { |
856 | this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] |
857 | { |
858 | implementation.getIdentifier(), Dependencies.class.getName() |
859 | } ), null ); |
860 | |
861 | } |
862 | |
863 | if ( decodedProperties != null ) |
864 | { |
865 | for ( Property decodedProperty : decodedProperties.getProperty() ) |
866 | { |
867 | final Property property = properties.getProperty( decodedProperty.getName() ); |
868 | |
869 | if ( property == null ) |
870 | { |
871 | report.getDetails().add( new ModelValidationReport.Detail( |
872 | "CLASS_MISSING_IMPLEMENTATION_PROPERTY", Level.SEVERE, |
873 | this.getMessage( "missingProperty", new Object[] |
874 | { |
875 | implementation.getIdentifier(), decodedProperty.getName() |
876 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
877 | |
878 | } |
879 | else |
880 | { |
881 | if ( decodedProperty.getType() == null |
882 | ? property.getType() != null |
883 | : !decodedProperty.getType().equals( property.getType() ) ) |
884 | { |
885 | report.getDetails().add( new ModelValidationReport.Detail( |
886 | "CLASS_ILLEGAL_IMPLEMENTATION_PROPERTY", Level.SEVERE, |
887 | this.getMessage( "illegalPropertyType", new Object[] |
888 | { |
889 | implementation.getIdentifier(), decodedProperty.getName(), |
890 | property.getType() == null ? "default" : property.getType(), |
891 | decodedProperty.getType() == null ? "default" : decodedProperty.getType() |
892 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
893 | |
894 | } |
895 | } |
896 | } |
897 | } |
898 | else if ( this.isLoggable( Level.WARNING ) ) |
899 | { |
900 | this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] |
901 | { |
902 | implementation.getIdentifier(), Properties.class.getName() |
903 | } ), null ); |
904 | |
905 | } |
906 | |
907 | if ( decodedMessages != null ) |
908 | { |
909 | for ( Message decodedMessage : decodedMessages.getMessage() ) |
910 | { |
911 | final Message message = messages.getMessage( decodedMessage.getName() ); |
912 | |
913 | if ( message == null ) |
914 | { |
915 | report.getDetails().add( new ModelValidationReport.Detail( |
916 | "CLASS_MISSING_IMPLEMENTATION_MESSAGE", Level.SEVERE, |
917 | this.getMessage( "missingMessage", new Object[] |
918 | { |
919 | implementation.getIdentifier(), decodedMessage.getName() |
920 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
921 | |
922 | } |
923 | } |
924 | } |
925 | else if ( this.isLoggable( Level.WARNING ) ) |
926 | { |
927 | this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] |
928 | { |
929 | implementation.getIdentifier(), Messages.class.getName() |
930 | } ), null ); |
931 | |
932 | } |
933 | |
934 | if ( decodedSpecifications != null ) |
935 | { |
936 | for ( Specification decodedSpecification : decodedSpecifications.getSpecification() ) |
937 | { |
938 | final Specification specification = |
939 | this.getModules().getSpecification( decodedSpecification.getIdentifier() ); |
940 | |
941 | if ( specification == null ) |
942 | { |
943 | report.getDetails().add( new ModelValidationReport.Detail( |
944 | "CLASS_MISSING_SPECIFICATION", Level.SEVERE, |
945 | this.getMessage( "missingSpecification", new Object[] |
946 | { |
947 | implementation.getIdentifier(), decodedSpecification.getIdentifier() |
948 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
949 | |
950 | } |
951 | else |
952 | { |
953 | if ( decodedSpecification.getMultiplicity() != specification.getMultiplicity() ) |
954 | { |
955 | report.getDetails().add( new ModelValidationReport.Detail( |
956 | "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, |
957 | this.getMessage( "illegalMultiplicity", new Object[] |
958 | { |
959 | specification.getIdentifier(), specification.getMultiplicity().value(), |
960 | decodedSpecification.getMultiplicity().value() |
961 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
962 | |
963 | } |
964 | |
965 | if ( decodedSpecification.getScope() == null |
966 | ? specification.getScope() != null |
967 | : !decodedSpecification.getScope().equals( specification.getScope() ) ) |
968 | { |
969 | report.getDetails().add( new ModelValidationReport.Detail( |
970 | "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, |
971 | this.getMessage( "illegalScope", new Object[] |
972 | { |
973 | decodedSpecification.getIdentifier(), |
974 | specification.getScope() == null |
975 | ? "Multiton" : specification.getScope(), |
976 | decodedSpecification.getScope() == null |
977 | ? "Multiton" : decodedSpecification.getScope() |
978 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
979 | |
980 | } |
981 | |
982 | if ( decodedSpecification.getClazz() == null |
983 | ? specification.getClazz() != null |
984 | : !decodedSpecification.getClazz().equals( specification.getClazz() ) ) |
985 | { |
986 | report.getDetails().add( new ModelValidationReport.Detail( |
987 | "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, |
988 | this.getMessage( "illegalSpecificationClass", new Object[] |
989 | { |
990 | decodedSpecification.getIdentifier(), specification.getClazz(), |
991 | decodedSpecification.getClazz() |
992 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
993 | |
994 | } |
995 | } |
996 | } |
997 | |
998 | for ( SpecificationReference decodedReference : decodedSpecifications.getReference() ) |
999 | { |
1000 | final Specification specification = |
1001 | specifications.getSpecification( decodedReference.getIdentifier() ); |
1002 | |
1003 | if ( specification == null ) |
1004 | { |
1005 | report.getDetails().add( new ModelValidationReport.Detail( |
1006 | "CLASS_MISSING_SPECIFICATION", Level.SEVERE, |
1007 | this.getMessage( "missingSpecification", new Object[] |
1008 | { |
1009 | implementation.getIdentifier(), decodedReference.getIdentifier() |
1010 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
1011 | |
1012 | } |
1013 | else if ( decodedReference.getVersion() != null && specification.getVersion() != null && |
1014 | VersionParser.compare( decodedReference.getVersion(), specification.getVersion() ) != 0 ) |
1015 | { |
1016 | final Module moduleOfSpecification = |
1017 | this.getModules().getModuleOfSpecification( decodedReference.getIdentifier() ); |
1018 | |
1019 | final Module moduleOfImplementation = |
1020 | this.getModules().getModuleOfImplementation( implementation.getIdentifier() ); |
1021 | |
1022 | report.getDetails().add( new ModelValidationReport.Detail( |
1023 | "CLASS_INCOMPATIBLE_IMPLEMENTATION", Level.SEVERE, |
1024 | this.getMessage( "incompatibleImplementation", new Object[] |
1025 | { |
1026 | javaClass.getClassName(), moduleOfImplementation == null |
1027 | ? "<>" : moduleOfImplementation.getName(), |
1028 | specification.getIdentifier(), moduleOfSpecification == null |
1029 | ? "<>" : moduleOfSpecification.getName(), |
1030 | decodedReference.getVersion(), specification.getVersion() |
1031 | } ), new ObjectFactory().createImplementation( implementation ) ) ); |
1032 | |
1033 | } |
1034 | } |
1035 | } |
1036 | else if ( this.isLoggable( Level.WARNING ) ) |
1037 | { |
1038 | this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[] |
1039 | { |
1040 | implementation.getIdentifier(), Specifications.class.getName() |
1041 | } ), null ); |
1042 | |
1043 | } |
1044 | |
1045 | return report; |
1046 | } |
1047 | catch ( final ParseException e ) |
1048 | { |
1049 | throw new ToolException( e ); |
1050 | } |
1051 | catch ( final TokenMgrError e ) |
1052 | { |
1053 | throw new ToolException( e ); |
1054 | } |
1055 | } |
1056 | |
1057 | /** |
1058 | * Transforms committed meta-data of compiled Java classes of the modules of the instance. |
1059 | * |
1060 | * @param marshaller The marshaller to use for transforming classes. |
1061 | * @param unmarshaller The unmarshaller to use for transforming classes. |
1062 | * @param classesDirectory The directory holding the compiled class files. |
1063 | * @param transformers The transformers to use for transforming the classes. |
1064 | * |
1065 | * @throws NullPointerException if {@code marshaller}, {@code unmarshaller}, {@code classesDirectory} or |
1066 | * {@code transformers} is {@code null}. |
1067 | * @throws ToolException if accessing class files fails. |
1068 | * |
1069 | * @see #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) |
1070 | */ |
1071 | public void transformClasses( final Marshaller marshaller, final Unmarshaller unmarshaller, |
1072 | final File classesDirectory, final List<Transformer> transformers ) |
1073 | throws ToolException |
1074 | { |
1075 | if ( marshaller == null ) |
1076 | { |
1077 | throw new NullPointerException( "marshaller" ); |
1078 | } |
1079 | if ( unmarshaller == null ) |
1080 | { |
1081 | throw new NullPointerException( "unmarshaller" ); |
1082 | } |
1083 | if ( transformers == null ) |
1084 | { |
1085 | throw new NullPointerException( "transformers" ); |
1086 | } |
1087 | if ( classesDirectory == null ) |
1088 | { |
1089 | throw new NullPointerException( "classesDirectory" ); |
1090 | } |
1091 | |
1092 | for ( Module m : this.getModules().getModule() ) |
1093 | { |
1094 | this.transformClasses( m, marshaller, unmarshaller, classesDirectory, transformers ); |
1095 | } |
1096 | } |
1097 | |
1098 | /** |
1099 | * Transforms committed meta-data of compiled Java classes of a given module of the modules of the instance. |
1100 | * |
1101 | * @param module The module to process. |
1102 | * @param marshaller The marshaller to use for transforming classes. |
1103 | * @param unmarshaller The unmarshaller to use for transforming classes. |
1104 | * @param classesDirectory The directory holding the compiled class files. |
1105 | * @param transformers The transformers to use for transforming the classes. |
1106 | * |
1107 | * @throws NullPointerException if {@code module}, {@code marshaller}, {@code unmarshaller}, |
1108 | * {@code classesDirectory} or {@code transformers} is {@code null}. |
1109 | * @throws ToolException if accessing class files fails. |
1110 | * |
1111 | * @see #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) |
1112 | * @see #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) |
1113 | */ |
1114 | public void transformClasses( final Module module, final Marshaller marshaller, final Unmarshaller unmarshaller, |
1115 | final File classesDirectory, final List<Transformer> transformers ) |
1116 | throws ToolException |
1117 | { |
1118 | if ( module == null ) |
1119 | { |
1120 | throw new NullPointerException( "module" ); |
1121 | } |
1122 | if ( marshaller == null ) |
1123 | { |
1124 | throw new NullPointerException( "marshaller" ); |
1125 | } |
1126 | if ( unmarshaller == null ) |
1127 | { |
1128 | throw new NullPointerException( "unmarshaller" ); |
1129 | } |
1130 | if ( transformers == null ) |
1131 | { |
1132 | throw new NullPointerException( "transformers" ); |
1133 | } |
1134 | if ( classesDirectory == null ) |
1135 | { |
1136 | throw new NullPointerException( "classesDirectory" ); |
1137 | } |
1138 | |
1139 | try |
1140 | { |
1141 | if ( module.getSpecifications() != null ) |
1142 | { |
1143 | for ( Specification s : module.getSpecifications().getSpecification() ) |
1144 | { |
1145 | if ( s.isClassDeclaration() ) |
1146 | { |
1147 | final String classLocation = s.getIdentifier().replace( '.', File.separatorChar ) + ".class"; |
1148 | final File classFile = new File( classesDirectory, classLocation ); |
1149 | |
1150 | if ( this.isLoggable( Level.INFO ) ) |
1151 | { |
1152 | this.log( Level.INFO, this.getMessage( "transforming", new Object[] |
1153 | { |
1154 | classFile.getAbsolutePath() |
1155 | } ), null ); |
1156 | |
1157 | } |
1158 | |
1159 | final JavaClass javaClass = this.getJavaClass( classFile ); |
1160 | this.transformClasses( s, marshaller, unmarshaller, javaClass, transformers ); |
1161 | javaClass.dump( classFile ); |
1162 | } |
1163 | } |
1164 | } |
1165 | |
1166 | if ( module.getImplementations() != null ) |
1167 | { |
1168 | for ( Implementation i : module.getImplementations().getImplementation() ) |
1169 | { |
1170 | if ( i.isClassDeclaration() ) |
1171 | { |
1172 | final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class"; |
1173 | final File classFile = new File( classesDirectory, classLocation ); |
1174 | |
1175 | if ( this.isLoggable( Level.INFO ) ) |
1176 | { |
1177 | this.log( Level.INFO, this.getMessage( "transforming", new Object[] |
1178 | { |
1179 | classFile.getAbsolutePath() |
1180 | } ), null ); |
1181 | |
1182 | } |
1183 | |
1184 | final JavaClass javaClass = this.getJavaClass( classFile ); |
1185 | this.transformClasses( i, marshaller, unmarshaller, javaClass, transformers ); |
1186 | javaClass.dump( classFile ); |
1187 | } |
1188 | } |
1189 | } |
1190 | } |
1191 | catch ( final IOException e ) |
1192 | { |
1193 | throw new ToolException( e ); |
1194 | } |
1195 | } |
1196 | |
1197 | /** |
1198 | * Transforms committed meta-data of compiled Java classes of a given specification of the modules of the instance. |
1199 | * |
1200 | * @param specification The specification to process. |
1201 | * @param marshaller The marshaller to use for transforming classes. |
1202 | * @param unmarshaller The unmarshaller to use for transforming classes. |
1203 | * @param javaClass The java class to process. |
1204 | * @param transformers The transformers to use for transforming the classes. |
1205 | * |
1206 | * @throws NullPointerException if {@code specification}, {@code marshaller}, {@code unmarshaller}, |
1207 | * {@code javaClass} or {@code transformers} is {@code null}. |
1208 | * @throws ToolException if accessing class files fails. |
1209 | * |
1210 | * @see org.jomc.model.ModelContext#createMarshaller() |
1211 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
1212 | */ |
1213 | public void transformClasses( final Specification specification, final Marshaller marshaller, |
1214 | final Unmarshaller unmarshaller, final JavaClass javaClass, |
1215 | final List<Transformer> transformers ) throws ToolException |
1216 | { |
1217 | if ( specification == null ) |
1218 | { |
1219 | throw new NullPointerException( "specification" ); |
1220 | } |
1221 | if ( marshaller == null ) |
1222 | { |
1223 | throw new NullPointerException( "marshaller" ); |
1224 | } |
1225 | if ( unmarshaller == null ) |
1226 | { |
1227 | throw new NullPointerException( "unmarshaller" ); |
1228 | } |
1229 | if ( javaClass == null ) |
1230 | { |
1231 | throw new NullPointerException( "javaClass" ); |
1232 | } |
1233 | if ( transformers == null ) |
1234 | { |
1235 | throw new NullPointerException( "transformers" ); |
1236 | } |
1237 | |
1238 | try |
1239 | { |
1240 | Specification decodedSpecification = null; |
1241 | final ObjectFactory objectFactory = new ObjectFactory(); |
1242 | final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() ); |
1243 | if ( bytes != null ) |
1244 | { |
1245 | decodedSpecification = this.decodeModelObject( unmarshaller, bytes, Specification.class ); |
1246 | } |
1247 | |
1248 | if ( decodedSpecification != null ) |
1249 | { |
1250 | for ( Transformer transformer : transformers ) |
1251 | { |
1252 | final JAXBSource source = |
1253 | new JAXBSource( marshaller, objectFactory.createSpecification( decodedSpecification ) ); |
1254 | |
1255 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1256 | transformer.transform( source, result ); |
1257 | decodedSpecification = ( (JAXBElement<Specification>) result.getResult() ).getValue(); |
1258 | } |
1259 | |
1260 | this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject( |
1261 | marshaller, objectFactory.createSpecification( decodedSpecification ) ) ); |
1262 | |
1263 | } |
1264 | } |
1265 | catch ( final JAXBException e ) |
1266 | { |
1267 | throw new ToolException( e ); |
1268 | } |
1269 | catch ( final TransformerException e ) |
1270 | { |
1271 | throw new ToolException( e ); |
1272 | } |
1273 | } |
1274 | |
1275 | /** |
1276 | * Transforms committed meta-data of compiled Java classes of a given implementation of the modules of the instance. |
1277 | * |
1278 | * @param implementation The implementation to process. |
1279 | * @param marshaller The marshaller to use for transforming classes. |
1280 | * @param unmarshaller The unmarshaller to use for transforming classes. |
1281 | * @param javaClass The java class to process. |
1282 | * @param transformers The transformers to use for transforming the classes. |
1283 | * |
1284 | * @throws NullPointerException if {@code implementation}, {@code marshaller}, {@code unmarshaller}, |
1285 | * {@code javaClass} or {@code transformers} is {@code null}. |
1286 | * @throws ToolException if accessing class files fails. |
1287 | * |
1288 | * @see org.jomc.model.ModelContext#createMarshaller() |
1289 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
1290 | */ |
1291 | public void transformClasses( final Implementation implementation, final Marshaller marshaller, |
1292 | final Unmarshaller unmarshaller, final JavaClass javaClass, |
1293 | final List<Transformer> transformers ) throws ToolException |
1294 | { |
1295 | if ( implementation == null ) |
1296 | { |
1297 | throw new NullPointerException( "implementation" ); |
1298 | } |
1299 | if ( marshaller == null ) |
1300 | { |
1301 | throw new NullPointerException( "marshaller" ); |
1302 | } |
1303 | if ( unmarshaller == null ) |
1304 | { |
1305 | throw new NullPointerException( "unmarshaller" ); |
1306 | } |
1307 | if ( javaClass == null ) |
1308 | { |
1309 | throw new NullPointerException( "javaClass" ); |
1310 | } |
1311 | if ( transformers == null ) |
1312 | { |
1313 | throw new NullPointerException( "transformers" ); |
1314 | } |
1315 | |
1316 | try |
1317 | { |
1318 | Dependencies decodedDependencies = null; |
1319 | byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() ); |
1320 | if ( bytes != null ) |
1321 | { |
1322 | decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class ); |
1323 | } |
1324 | |
1325 | Messages decodedMessages = null; |
1326 | bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() ); |
1327 | if ( bytes != null ) |
1328 | { |
1329 | decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class ); |
1330 | } |
1331 | |
1332 | Properties decodedProperties = null; |
1333 | bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() ); |
1334 | if ( bytes != null ) |
1335 | { |
1336 | decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class ); |
1337 | } |
1338 | |
1339 | Specifications decodedSpecifications = null; |
1340 | bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() ); |
1341 | if ( bytes != null ) |
1342 | { |
1343 | decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class ); |
1344 | } |
1345 | |
1346 | final ObjectFactory of = new ObjectFactory(); |
1347 | for ( Transformer transformer : transformers ) |
1348 | { |
1349 | if ( decodedDependencies != null ) |
1350 | { |
1351 | final JAXBSource source = new JAXBSource( marshaller, of.createDependencies( decodedDependencies ) ); |
1352 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1353 | transformer.transform( source, result ); |
1354 | decodedDependencies = ( (JAXBElement<Dependencies>) result.getResult() ).getValue(); |
1355 | } |
1356 | |
1357 | if ( decodedMessages != null ) |
1358 | { |
1359 | final JAXBSource source = new JAXBSource( marshaller, of.createMessages( decodedMessages ) ); |
1360 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1361 | transformer.transform( source, result ); |
1362 | decodedMessages = ( (JAXBElement<Messages>) result.getResult() ).getValue(); |
1363 | } |
1364 | |
1365 | if ( decodedProperties != null ) |
1366 | { |
1367 | final JAXBSource source = new JAXBSource( marshaller, of.createProperties( decodedProperties ) ); |
1368 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1369 | transformer.transform( source, result ); |
1370 | decodedProperties = ( (JAXBElement<Properties>) result.getResult() ).getValue(); |
1371 | } |
1372 | |
1373 | if ( decodedSpecifications != null ) |
1374 | { |
1375 | final JAXBSource source = |
1376 | new JAXBSource( marshaller, of.createSpecifications( decodedSpecifications ) ); |
1377 | |
1378 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1379 | transformer.transform( source, result ); |
1380 | decodedSpecifications = ( (JAXBElement<Specifications>) result.getResult() ).getValue(); |
1381 | } |
1382 | } |
1383 | |
1384 | if ( decodedDependencies != null ) |
1385 | { |
1386 | this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject( |
1387 | marshaller, of.createDependencies( decodedDependencies ) ) ); |
1388 | |
1389 | } |
1390 | |
1391 | if ( decodedMessages != null ) |
1392 | { |
1393 | this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject( |
1394 | marshaller, of.createMessages( decodedMessages ) ) ); |
1395 | |
1396 | } |
1397 | |
1398 | if ( decodedProperties != null ) |
1399 | { |
1400 | this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject( |
1401 | marshaller, of.createProperties( decodedProperties ) ) ); |
1402 | |
1403 | } |
1404 | |
1405 | if ( decodedSpecifications != null ) |
1406 | { |
1407 | this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject( |
1408 | marshaller, of.createSpecifications( decodedSpecifications ) ) ); |
1409 | |
1410 | } |
1411 | } |
1412 | catch ( final JAXBException e ) |
1413 | { |
1414 | throw new ToolException( e ); |
1415 | } |
1416 | catch ( final TransformerException e ) |
1417 | { |
1418 | throw new ToolException( e ); |
1419 | } |
1420 | } |
1421 | |
1422 | /** |
1423 | * Parses a class file. |
1424 | * |
1425 | * @param classFile The class file to parse. |
1426 | * |
1427 | * @return The parsed class file. |
1428 | * |
1429 | * @throws NullPointerException if {@code classFile} is {@code null}. |
1430 | * @throws ToolException if parsing {@code classFile} fails. |
1431 | * |
1432 | * @see JavaClass |
1433 | */ |
1434 | public JavaClass getJavaClass( final File classFile ) throws ToolException |
1435 | { |
1436 | if ( classFile == null ) |
1437 | { |
1438 | throw new NullPointerException( "classFile" ); |
1439 | } |
1440 | |
1441 | try |
1442 | { |
1443 | return this.getJavaClass( classFile.toURI().toURL(), classFile.getName() ); |
1444 | } |
1445 | catch ( final IOException e ) |
1446 | { |
1447 | throw new ToolException( e ); |
1448 | } |
1449 | } |
1450 | |
1451 | /** |
1452 | * Parses a class file. |
1453 | * |
1454 | * @param url The URL of the class file to parse. |
1455 | * @param className The name of the class at {@code url}. |
1456 | * |
1457 | * @return The parsed class file. |
1458 | * |
1459 | * @throws NullPointerException if {@code url} or {@code className} is {@code null}. |
1460 | * @throws ToolException if parsing fails. |
1461 | * |
1462 | * @see JavaClass |
1463 | */ |
1464 | public JavaClass getJavaClass( final URL url, final String className ) throws ToolException |
1465 | { |
1466 | if ( url == null ) |
1467 | { |
1468 | throw new NullPointerException( "url" ); |
1469 | } |
1470 | if ( className == null ) |
1471 | { |
1472 | throw new NullPointerException( "className" ); |
1473 | } |
1474 | |
1475 | try |
1476 | { |
1477 | return this.getJavaClass( url.openStream(), className ); |
1478 | } |
1479 | catch ( final IOException e ) |
1480 | { |
1481 | throw new ToolException( e ); |
1482 | } |
1483 | } |
1484 | |
1485 | /** |
1486 | * Parses a class file. |
1487 | * |
1488 | * @param stream The stream to read the class file from. |
1489 | * @param className The name of the class to read from {@code stream}. |
1490 | * |
1491 | * @return The parsed class file. |
1492 | * |
1493 | * @throws NullPointerException if {@code stream} or {@code className} is {@code null}. |
1494 | * @throws ToolException if parsing fails. |
1495 | * |
1496 | * @see JavaClass |
1497 | */ |
1498 | public JavaClass getJavaClass( final InputStream stream, final String className ) throws ToolException |
1499 | { |
1500 | if ( stream == null ) |
1501 | { |
1502 | throw new NullPointerException( "stream" ); |
1503 | } |
1504 | if ( className == null ) |
1505 | { |
1506 | throw new NullPointerException( "className" ); |
1507 | } |
1508 | |
1509 | try |
1510 | { |
1511 | final ClassParser parser = new ClassParser( stream, className ); |
1512 | final JavaClass clazz = parser.parse(); |
1513 | stream.close(); |
1514 | return clazz; |
1515 | } |
1516 | catch ( final IOException e ) |
1517 | { |
1518 | throw new ToolException( e ); |
1519 | } |
1520 | } |
1521 | |
1522 | /** |
1523 | * Gets an attribute from a java class. |
1524 | * |
1525 | * @param clazz The java class to get an attribute from. |
1526 | * @param attributeName The name of the attribute to get. |
1527 | * |
1528 | * @return The value of attribute {@code attributeName} of {@code clazz} or {@code null} if no such attribute |
1529 | * exists. |
1530 | * |
1531 | * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}. |
1532 | * @throws ToolException if getting the attribute fails. |
1533 | * |
1534 | * @see JavaClass#getAttributes() |
1535 | */ |
1536 | public byte[] getClassfileAttribute( final JavaClass clazz, final String attributeName ) throws ToolException |
1537 | { |
1538 | if ( clazz == null ) |
1539 | { |
1540 | throw new NullPointerException( "clazz" ); |
1541 | } |
1542 | if ( attributeName == null ) |
1543 | { |
1544 | throw new NullPointerException( "attributeName" ); |
1545 | } |
1546 | |
1547 | final Attribute[] attributes = clazz.getAttributes(); |
1548 | |
1549 | for ( int i = attributes.length - 1; i >= 0; i-- ) |
1550 | { |
1551 | final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() ); |
1552 | |
1553 | if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) ) |
1554 | { |
1555 | final Unknown unknown = (Unknown) attributes[i]; |
1556 | return unknown.getBytes(); |
1557 | } |
1558 | } |
1559 | |
1560 | return null; |
1561 | } |
1562 | |
1563 | /** |
1564 | * Adds or updates an attribute in a java class. |
1565 | * |
1566 | * @param clazz The class to update. |
1567 | * @param attributeName The name of the attribute to update. |
1568 | * @param data The new data of the attribute to update the {@code classFile} with. |
1569 | * |
1570 | * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}. |
1571 | * @throws ToolException if updating the class file fails. |
1572 | * |
1573 | * @see JavaClass#getAttributes() |
1574 | */ |
1575 | public void setClassfileAttribute( final JavaClass clazz, final String attributeName, final byte[] data ) |
1576 | throws ToolException |
1577 | { |
1578 | if ( clazz == null ) |
1579 | { |
1580 | throw new NullPointerException( "clazz" ); |
1581 | } |
1582 | if ( attributeName == null ) |
1583 | { |
1584 | throw new NullPointerException( "attributeName" ); |
1585 | } |
1586 | |
1587 | /* |
1588 | The JavaTM Virtual Machine Specification - Second Edition - Chapter 4.1 |
1589 | |
1590 | A Java virtual machine implementation is required to silently ignore any |
1591 | or all attributes in the attributes table of a ClassFile structure that |
1592 | it does not recognize. Attributes not defined in this specification are |
1593 | not allowed to affect the semantics of the class file, but only to |
1594 | provide additional descriptive information (§4.7.1). |
1595 | */ |
1596 | Attribute[] attributes = clazz.getAttributes(); |
1597 | |
1598 | int attributeIndex = -1; |
1599 | int nameIndex = -1; |
1600 | |
1601 | for ( int i = attributes.length - 1; i >= 0; i-- ) |
1602 | { |
1603 | final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() ); |
1604 | |
1605 | if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) ) |
1606 | { |
1607 | attributeIndex = i; |
1608 | nameIndex = attributes[i].getNameIndex(); |
1609 | } |
1610 | } |
1611 | |
1612 | if ( nameIndex == -1 ) |
1613 | { |
1614 | final Constant[] pool = clazz.getConstantPool().getConstantPool(); |
1615 | final Constant[] tmp = new Constant[ pool.length + 1 ]; |
1616 | System.arraycopy( pool, 0, tmp, 0, pool.length ); |
1617 | tmp[pool.length] = new ConstantUtf8( attributeName ); |
1618 | nameIndex = pool.length; |
1619 | clazz.setConstantPool( new ConstantPool( tmp ) ); |
1620 | } |
1621 | |
1622 | final Unknown unknown = new Unknown( nameIndex, data.length, data, clazz.getConstantPool() ); |
1623 | |
1624 | if ( attributeIndex == -1 ) |
1625 | { |
1626 | final Attribute[] tmp = new Attribute[ attributes.length + 1 ]; |
1627 | System.arraycopy( attributes, 0, tmp, 0, attributes.length ); |
1628 | tmp[attributes.length] = unknown; |
1629 | attributes = tmp; |
1630 | } |
1631 | else |
1632 | { |
1633 | attributes[attributeIndex] = unknown; |
1634 | } |
1635 | |
1636 | clazz.setAttributes( attributes ); |
1637 | } |
1638 | |
1639 | /** |
1640 | * Encodes a model object to a byte array. |
1641 | * |
1642 | * @param marshaller The marshaller to use for encoding the object. |
1643 | * @param modelObject The model object to encode. |
1644 | * |
1645 | * @return GZIP compressed XML document for {@code modelObject}. |
1646 | * |
1647 | * @throws NullPointerException if {@code marshaller} or {@code modelObject} is {@code null}. |
1648 | * @throws ToolException if encoding {@code modelObject} fails. |
1649 | */ |
1650 | public byte[] encodeModelObject( final Marshaller marshaller, final JAXBElement<? extends ModelObject> modelObject ) |
1651 | throws ToolException |
1652 | { |
1653 | if ( marshaller == null ) |
1654 | { |
1655 | throw new NullPointerException( "marshaller" ); |
1656 | } |
1657 | if ( modelObject == null ) |
1658 | { |
1659 | throw new NullPointerException( "modelObject" ); |
1660 | } |
1661 | |
1662 | try |
1663 | { |
1664 | final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
1665 | final GZIPOutputStream out = new GZIPOutputStream( baos ); |
1666 | marshaller.marshal( modelObject, out ); |
1667 | out.close(); |
1668 | return baos.toByteArray(); |
1669 | } |
1670 | catch ( final JAXBException e ) |
1671 | { |
1672 | throw new ToolException( e ); |
1673 | } |
1674 | catch ( final IOException e ) |
1675 | { |
1676 | throw new ToolException( e ); |
1677 | } |
1678 | } |
1679 | |
1680 | /** |
1681 | * Decodes a model object from a byte array. |
1682 | * |
1683 | * @param unmarshaller The unmarshaller to use for decoding the object. |
1684 | * @param bytes The encoded model object to decode. |
1685 | * @param type The type of the encoded model object. |
1686 | * @param <T> The type of the decoded model object. |
1687 | * |
1688 | * @return Model object decoded from {@code bytes}. |
1689 | * |
1690 | * @throws NullPointerException if {@code unmarshaller}, {@code bytes} or {@code type} is {@code null}. |
1691 | * @throws ToolException if decoding {@code bytes} fails. |
1692 | */ |
1693 | public <T extends ModelObject> T decodeModelObject( final Unmarshaller unmarshaller, final byte[] bytes, |
1694 | final Class<T> type ) throws ToolException |
1695 | { |
1696 | if ( unmarshaller == null ) |
1697 | { |
1698 | throw new NullPointerException( "unmarshaller" ); |
1699 | } |
1700 | if ( bytes == null ) |
1701 | { |
1702 | throw new NullPointerException( "bytes" ); |
1703 | } |
1704 | if ( type == null ) |
1705 | { |
1706 | throw new NullPointerException( "type" ); |
1707 | } |
1708 | |
1709 | try |
1710 | { |
1711 | final ByteArrayInputStream bais = new ByteArrayInputStream( bytes ); |
1712 | final GZIPInputStream in = new GZIPInputStream( bais ); |
1713 | final JAXBElement<T> element = (JAXBElement<T>) unmarshaller.unmarshal( in ); |
1714 | in.close(); |
1715 | return element.getValue(); |
1716 | } |
1717 | catch ( final JAXBException e ) |
1718 | { |
1719 | throw new ToolException( e ); |
1720 | } |
1721 | catch ( final IOException e ) |
1722 | { |
1723 | throw new ToolException( e ); |
1724 | } |
1725 | } |
1726 | |
1727 | private String getMessage( final String key, final Object args ) |
1728 | { |
1729 | final ResourceBundle b = ResourceBundle.getBundle( JavaClasses.class.getName().replace( '.', '/' ) ); |
1730 | final MessageFormat f = new MessageFormat( b.getString( key ) ); |
1731 | return f.format( args ); |
1732 | } |
1733 | |
1734 | } |