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 1435 2010-01-30 01:52:40Z 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 1435 2010-01-30 01:52:40Z 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, getMessage( "committing", classFile.getAbsolutePath() ), null ); |
235 | } |
236 | |
237 | final JavaClass javaClass = this.getJavaClass( classFile ); |
238 | this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject( |
239 | marshaller, new ObjectFactory().createSpecification( specification ) ) ); |
240 | |
241 | javaClass.dump( classFile ); |
242 | } |
243 | } |
244 | catch ( final IOException e ) |
245 | { |
246 | throw new ToolException( e ); |
247 | } |
248 | } |
249 | |
250 | /** |
251 | * Commits meta-data of a given implementation of the modules of the instance to compiled Java classes. |
252 | * |
253 | * @param implementation The implementation to process. |
254 | * @param marshaller The marshaller to use for committing the classes. |
255 | * @param classesDirectory The directory holding the compiled class files. |
256 | * |
257 | * @throws NullPointerException if {@code implementation}, {@code marshaller} or {@code classesDirectory} is |
258 | * {@code null}. |
259 | * @throws ToolException if committing meta-data fails. |
260 | * |
261 | * @see org.jomc.model.ModelContext#createMarshaller() |
262 | */ |
263 | public void commitClasses( final Implementation implementation, final Marshaller marshaller, |
264 | final File classesDirectory ) throws ToolException |
265 | { |
266 | if ( implementation == null ) |
267 | { |
268 | throw new NullPointerException( "implementation" ); |
269 | } |
270 | if ( marshaller == null ) |
271 | { |
272 | throw new NullPointerException( "marshaller" ); |
273 | } |
274 | if ( classesDirectory == null ) |
275 | { |
276 | throw new NullPointerException( "classesDirectory" ); |
277 | } |
278 | |
279 | try |
280 | { |
281 | if ( implementation.isClassDeclaration() ) |
282 | { |
283 | Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() ); |
284 | if ( dependencies == null ) |
285 | { |
286 | dependencies = new Dependencies(); |
287 | } |
288 | |
289 | Properties properties = this.getModules().getProperties( implementation.getIdentifier() ); |
290 | if ( properties == null ) |
291 | { |
292 | properties = new Properties(); |
293 | } |
294 | |
295 | Messages messages = this.getModules().getMessages( implementation.getIdentifier() ); |
296 | if ( messages == null ) |
297 | { |
298 | messages = new Messages(); |
299 | } |
300 | |
301 | Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() ); |
302 | if ( specifications == null ) |
303 | { |
304 | specifications = new Specifications(); |
305 | } |
306 | |
307 | for ( SpecificationReference r : specifications.getReference() ) |
308 | { |
309 | if ( specifications.getSpecification( r.getIdentifier() ) == null && |
310 | this.isLoggable( Level.WARNING ) ) |
311 | { |
312 | this.log( Level.WARNING, getMessage( "unresolvedSpecification", r.getIdentifier(), |
313 | implementation.getIdentifier() ), null ); |
314 | |
315 | } |
316 | } |
317 | |
318 | for ( Dependency d : dependencies.getDependency() ) |
319 | { |
320 | final Specification s = this.getModules().getSpecification( d.getIdentifier() ); |
321 | |
322 | if ( s != null ) |
323 | { |
324 | if ( specifications.getSpecification( s.getIdentifier() ) == null ) |
325 | { |
326 | specifications.getSpecification().add( s ); |
327 | } |
328 | } |
329 | else if ( this.isLoggable( Level.WARNING ) ) |
330 | { |
331 | this.log( Level.WARNING, getMessage( "unresolvedDependencySpecification", d.getIdentifier(), |
332 | d.getName(), implementation.getIdentifier() ), null ); |
333 | |
334 | } |
335 | } |
336 | |
337 | final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class"; |
338 | final File classFile = new File( classesDirectory, classLocation ); |
339 | |
340 | if ( this.isLoggable( Level.INFO ) ) |
341 | { |
342 | this.log( Level.INFO, getMessage( "committing", classFile.getAbsolutePath() ), null ); |
343 | } |
344 | |
345 | final JavaClass javaClass = this.getJavaClass( classFile ); |
346 | final ObjectFactory of = new ObjectFactory(); |
347 | |
348 | this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject( |
349 | marshaller, of.createDependencies( dependencies ) ) ); |
350 | |
351 | this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject( |
352 | marshaller, of.createProperties( properties ) ) ); |
353 | |
354 | this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject( |
355 | marshaller, of.createMessages( messages ) ) ); |
356 | |
357 | this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject( |
358 | marshaller, of.createSpecifications( specifications ) ) ); |
359 | |
360 | javaClass.dump( classFile ); |
361 | } |
362 | } |
363 | catch ( final IOException e ) |
364 | { |
365 | throw new ToolException( e ); |
366 | } |
367 | } |
368 | |
369 | /** |
370 | * Validates compiled Java classes against the modules of the instance. |
371 | * |
372 | * @param unmarshaller The unmarshaller to use for validating classes. |
373 | * @param classesDirectory The directory holding the compiled class files. |
374 | * |
375 | * @return The report of the validation. |
376 | * |
377 | * @throws NullPointerException if {@code unmarshaller} or {@code classesDirectory} is {@code null}. |
378 | * @throws ToolException if reading class files fails. |
379 | * |
380 | * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) |
381 | */ |
382 | public ModelValidationReport validateClasses( final Unmarshaller unmarshaller, final File classesDirectory ) |
383 | throws ToolException |
384 | { |
385 | if ( unmarshaller == null ) |
386 | { |
387 | throw new NullPointerException( "unmarshaller" ); |
388 | } |
389 | if ( classesDirectory == null ) |
390 | { |
391 | throw new NullPointerException( "classesDirectory" ); |
392 | } |
393 | |
394 | final ModelValidationReport report = new ModelValidationReport(); |
395 | |
396 | for ( Module m : this.getModules().getModule() ) |
397 | { |
398 | final ModelValidationReport current = this.validateClasses( m, unmarshaller, classesDirectory ); |
399 | report.getDetails().addAll( current.getDetails() ); |
400 | } |
401 | |
402 | return report; |
403 | } |
404 | |
405 | /** |
406 | * Validates compiled Java classes against the modules of the instance. |
407 | * |
408 | * @param unmarshaller The unmarshaller to use for validating classes. |
409 | * @param classLoader The class loader to search for classes. |
410 | * |
411 | * @return The report of the validation. |
412 | * |
413 | * @throws NullPointerException if {@code unmarshaller} or {@code classLoader} is {@code null}. |
414 | * @throws ToolException if reading class files fails. |
415 | * |
416 | * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) |
417 | */ |
418 | public ModelValidationReport validateClasses( final Unmarshaller unmarshaller, final ClassLoader classLoader ) |
419 | throws ToolException |
420 | { |
421 | if ( unmarshaller == null ) |
422 | { |
423 | throw new NullPointerException( "unmarshaller" ); |
424 | } |
425 | if ( classLoader == null ) |
426 | { |
427 | throw new NullPointerException( "classLoader" ); |
428 | } |
429 | |
430 | final ModelValidationReport report = new ModelValidationReport(); |
431 | |
432 | for ( Module m : this.getModules().getModule() ) |
433 | { |
434 | final ModelValidationReport current = this.validateClasses( m, unmarshaller, classLoader ); |
435 | report.getDetails().addAll( current.getDetails() ); |
436 | } |
437 | |
438 | return report; |
439 | } |
440 | |
441 | /** |
442 | * Validates compiled Java classes against a given module of the modules of the instance. |
443 | * |
444 | * @param module The module to process. |
445 | * @param unmarshaller The unmarshaller to use for validating classes. |
446 | * @param classesDirectory The directory holding the compiled class files. |
447 | * |
448 | * @return The report of the validation. |
449 | * |
450 | * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classesDirectory} is {@code null}. |
451 | * @throws ToolException if reading class files fails. |
452 | * |
453 | * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
454 | * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
455 | */ |
456 | public ModelValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller, |
457 | final File classesDirectory ) throws ToolException |
458 | { |
459 | if ( module == null ) |
460 | { |
461 | throw new NullPointerException( "module" ); |
462 | } |
463 | if ( unmarshaller == null ) |
464 | { |
465 | throw new NullPointerException( "unmarshaller" ); |
466 | } |
467 | if ( classesDirectory == null ) |
468 | { |
469 | throw new NullPointerException( "classesDirectory" ); |
470 | } |
471 | |
472 | final ModelValidationReport report = new ModelValidationReport(); |
473 | |
474 | if ( module.getSpecifications() != null ) |
475 | { |
476 | for ( Specification s : module.getSpecifications().getSpecification() ) |
477 | { |
478 | if ( s.isClassDeclaration() ) |
479 | { |
480 | final String classLocation = s.getClazz().replace( '.', File.separatorChar ) + ".class"; |
481 | final File classFile = new File( classesDirectory, classLocation ); |
482 | final ModelValidationReport current = |
483 | this.validateClasses( s, unmarshaller, this.getJavaClass( classFile ) ); |
484 | |
485 | report.getDetails().addAll( current.getDetails() ); |
486 | } |
487 | } |
488 | } |
489 | |
490 | if ( module.getImplementations() != null ) |
491 | { |
492 | for ( Implementation i : module.getImplementations().getImplementation() ) |
493 | { |
494 | if ( i.isClassDeclaration() ) |
495 | { |
496 | final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class"; |
497 | final File classFile = new File( classesDirectory, classLocation ); |
498 | final JavaClass javaClass = this.getJavaClass( classFile ); |
499 | final ModelValidationReport current = |
500 | this.validateClasses( i, unmarshaller, javaClass ); |
501 | |
502 | report.getDetails().addAll( current.getDetails() ); |
503 | } |
504 | } |
505 | } |
506 | |
507 | return report; |
508 | } |
509 | |
510 | /** |
511 | * Validates compiled Java classes against a given module of the modules of the instance. |
512 | * |
513 | * @param module The module to process. |
514 | * @param unmarshaller The unmarshaller to use for validating classes. |
515 | * @param classLoader The class loader to search for classes. |
516 | * |
517 | * @return The report of the validation. |
518 | * |
519 | * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classLoader} is {@code null}. |
520 | * @throws ToolException if reading class files fails. |
521 | * |
522 | * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
523 | * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) |
524 | */ |
525 | public ModelValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller, |
526 | final ClassLoader classLoader ) throws ToolException |
527 | { |
528 | if ( module == null ) |
529 | { |
530 | throw new NullPointerException( "module" ); |
531 | } |
532 | if ( unmarshaller == null ) |
533 | { |
534 | throw new NullPointerException( "unmarshaller" ); |
535 | } |
536 | if ( classLoader == null ) |
537 | { |
538 | throw new NullPointerException( "classLoader" ); |
539 | } |
540 | |
541 | final ModelValidationReport report = new ModelValidationReport(); |
542 | |
543 | if ( module.getSpecifications() != null ) |
544 | { |
545 | for ( Specification s : module.getSpecifications().getSpecification() ) |
546 | { |
547 | if ( s.isClassDeclaration() ) |
548 | { |
549 | final String classLocation = s.getClazz().replace( '.', '/' ) + ".class"; |
550 | final URL classUrl = classLoader.getResource( classLocation ); |
551 | |
552 | if ( classUrl == null ) |
553 | { |
554 | throw new ToolException( getMessage( "resourceNotFound", classLocation ) ); |
555 | } |
556 | |
557 | final JavaClass javaClass = this.getJavaClass( classUrl, classLocation ); |
558 | final ModelValidationReport current = |
559 | this.validateClasses( s, unmarshaller, javaClass ); |
560 | |
561 | report.getDetails().addAll( current.getDetails() ); |
562 | } |
563 | } |
564 | } |
565 | |
566 | if ( module.getImplementations() != null ) |
567 | { |
568 | for ( Implementation i : module.getImplementations().getImplementation() ) |
569 | { |
570 | if ( i.isClassDeclaration() ) |
571 | { |
572 | final String classLocation = i.getClazz().replace( '.', '/' ) + ".class"; |
573 | final URL classUrl = classLoader.getResource( classLocation ); |
574 | |
575 | if ( classUrl == null ) |
576 | { |
577 | throw new ToolException( getMessage( "resourceNotFound", classLocation ) ); |
578 | } |
579 | |
580 | final JavaClass javaClass = this.getJavaClass( classUrl, classLocation ); |
581 | final ModelValidationReport current = this.validateClasses( i, unmarshaller, javaClass ); |
582 | report.getDetails().addAll( current.getDetails() ); |
583 | } |
584 | } |
585 | } |
586 | |
587 | return report; |
588 | } |
589 | |
590 | /** |
591 | * Validates compiled Java classes against a given specification of the modules of the instance. |
592 | * |
593 | * @param specification The specification to process. |
594 | * @param unmarshaller The unmarshaller to use for validating classes. |
595 | * @param javaClass The class to validate. |
596 | * |
597 | * @return The report of the validation. |
598 | * |
599 | * @throws NullPointerException if {@code specification}, {@code unmarshaller} or {@code javaClass} is {@code null}. |
600 | * @throws ToolException if reading class files fails. |
601 | * |
602 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
603 | */ |
604 | public ModelValidationReport validateClasses( final Specification specification, |
605 | final Unmarshaller unmarshaller, final JavaClass javaClass ) |
606 | throws ToolException |
607 | { |
608 | if ( specification == null ) |
609 | { |
610 | throw new NullPointerException( "specification" ); |
611 | } |
612 | if ( unmarshaller == null ) |
613 | { |
614 | throw new NullPointerException( "unmarshaller" ); |
615 | } |
616 | if ( javaClass == null ) |
617 | { |
618 | throw new NullPointerException( "javaClass" ); |
619 | } |
620 | |
621 | if ( this.isLoggable( Level.INFO ) ) |
622 | { |
623 | this.log( Level.INFO, getMessage( "validatingSpecification", specification.getIdentifier() ), null ); |
624 | } |
625 | |
626 | final ModelValidationReport report = new ModelValidationReport(); |
627 | |
628 | Specification decoded = null; |
629 | final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() ); |
630 | if ( bytes != null ) |
631 | { |
632 | decoded = this.decodeModelObject( unmarshaller, bytes, Specification.class ); |
633 | } |
634 | |
635 | if ( decoded != null ) |
636 | { |
637 | if ( decoded.getMultiplicity() != specification.getMultiplicity() ) |
638 | { |
639 | report.getDetails().add( new ModelValidationReport.Detail( |
640 | "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, getMessage( |
641 | "illegalMultiplicity", specification.getIdentifier(), specification.getMultiplicity().value(), |
642 | decoded.getMultiplicity().value() ), new ObjectFactory().createSpecification( specification ) ) ); |
643 | |
644 | } |
645 | |
646 | if ( decoded.getScope() == null |
647 | ? specification.getScope() != null |
648 | : !decoded.getScope().equals( specification.getScope() ) ) |
649 | { |
650 | report.getDetails().add( new ModelValidationReport.Detail( |
651 | "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, getMessage( |
652 | "illegalScope", specification.getIdentifier(), |
653 | specification.getScope() == null ? "Multiton" : specification.getScope(), |
654 | decoded.getScope() == null ? "Multiton" : decoded.getScope() ), |
655 | new ObjectFactory().createSpecification( specification ) ) ); |
656 | |
657 | } |
658 | |
659 | if ( decoded.getClazz() == null |
660 | ? specification.getClazz() != null |
661 | : !decoded.getClazz().equals( specification.getClazz() ) ) |
662 | { |
663 | report.getDetails().add( new ModelValidationReport.Detail( |
664 | "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, getMessage( |
665 | "illegalSpecificationClass", decoded.getIdentifier(), |
666 | specification.getClazz(), decoded.getClazz() ), |
667 | new ObjectFactory().createSpecification( specification ) ) ); |
668 | |
669 | } |
670 | } |
671 | else if ( this.isLoggable( Level.WARNING ) ) |
672 | { |
673 | this.log( Level.WARNING, getMessage( "cannotValidateSpecification", specification.getIdentifier(), |
674 | Specification.class.getName() ), null ); |
675 | |
676 | } |
677 | |
678 | return report; |
679 | } |
680 | |
681 | /** |
682 | * Validates compiled Java classes against a given implementation of the modules of the instance. |
683 | * |
684 | * @param implementation The implementation to process. |
685 | * @param unmarshaller The unmarshaller to use for validating classes. |
686 | * @param javaClass The class to validate. |
687 | * |
688 | * @return The report of the validation. |
689 | * |
690 | * @throws NullPointerException if {@code implementation}, {@code unmarshaller} or {@code javaClass} is |
691 | * {@code null}. |
692 | * @throws ToolException if reading class files fails. |
693 | * |
694 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
695 | */ |
696 | public ModelValidationReport validateClasses( final Implementation implementation, |
697 | final Unmarshaller unmarshaller, final JavaClass javaClass ) |
698 | throws ToolException |
699 | { |
700 | if ( implementation == null ) |
701 | { |
702 | throw new NullPointerException( "implementation" ); |
703 | } |
704 | if ( unmarshaller == null ) |
705 | { |
706 | throw new NullPointerException( "unmarshaller" ); |
707 | } |
708 | if ( javaClass == null ) |
709 | { |
710 | throw new NullPointerException( "javaClass" ); |
711 | } |
712 | |
713 | if ( this.isLoggable( Level.INFO ) ) |
714 | { |
715 | this.log( Level.INFO, getMessage( "validatingImplementation", implementation.getIdentifier() ), null ); |
716 | } |
717 | |
718 | final ModelValidationReport report = new ModelValidationReport(); |
719 | |
720 | try |
721 | { |
722 | Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() ); |
723 | if ( dependencies == null ) |
724 | { |
725 | dependencies = new Dependencies(); |
726 | } |
727 | |
728 | Properties properties = this.getModules().getProperties( implementation.getIdentifier() ); |
729 | if ( properties == null ) |
730 | { |
731 | properties = new Properties(); |
732 | } |
733 | |
734 | Messages messages = this.getModules().getMessages( implementation.getIdentifier() ); |
735 | if ( messages == null ) |
736 | { |
737 | messages = new Messages(); |
738 | } |
739 | |
740 | Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() ); |
741 | if ( specifications == null ) |
742 | { |
743 | specifications = new Specifications(); |
744 | } |
745 | |
746 | Dependencies decodedDependencies = null; |
747 | byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() ); |
748 | if ( bytes != null ) |
749 | { |
750 | decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class ); |
751 | } |
752 | |
753 | Properties decodedProperties = null; |
754 | bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() ); |
755 | if ( bytes != null ) |
756 | { |
757 | decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class ); |
758 | } |
759 | |
760 | Messages decodedMessages = null; |
761 | bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() ); |
762 | if ( bytes != null ) |
763 | { |
764 | decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class ); |
765 | } |
766 | |
767 | Specifications decodedSpecifications = null; |
768 | bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() ); |
769 | if ( bytes != null ) |
770 | { |
771 | decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class ); |
772 | } |
773 | |
774 | if ( decodedDependencies != null ) |
775 | { |
776 | for ( Dependency decodedDependency : decodedDependencies.getDependency() ) |
777 | { |
778 | final Dependency dependency = dependencies.getDependency( decodedDependency.getName() ); |
779 | |
780 | if ( dependency == null ) |
781 | { |
782 | report.getDetails().add( new ModelValidationReport.Detail( |
783 | "CLASS_MISSING_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, getMessage( |
784 | "missingDependency", implementation.getIdentifier(), decodedDependency.getName() ), |
785 | new ObjectFactory().createImplementation( implementation ) ) ); |
786 | |
787 | } |
788 | |
789 | final Specification s = this.getModules().getSpecification( decodedDependency.getIdentifier() ); |
790 | |
791 | if ( s != null && s.getVersion() != null && decodedDependency.getVersion() != null && |
792 | VersionParser.compare( decodedDependency.getVersion(), s.getVersion() ) > 0 ) |
793 | { |
794 | final Module moduleOfSpecification = |
795 | this.getModules().getModuleOfSpecification( s.getIdentifier() ); |
796 | |
797 | final Module moduleOfImplementation = |
798 | this.getModules().getModuleOfImplementation( implementation.getIdentifier() ); |
799 | |
800 | report.getDetails().add( new ModelValidationReport.Detail( |
801 | "CLASS_INCOMPATIBLE_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, getMessage( |
802 | "incompatibleDependency", javaClass.getClassName(), |
803 | moduleOfImplementation == null ? "<>" : moduleOfImplementation.getName(), |
804 | s.getIdentifier(), |
805 | moduleOfSpecification == null ? "<>" : moduleOfSpecification.getName(), |
806 | decodedDependency.getVersion(), s.getVersion() ), |
807 | new ObjectFactory().createImplementation( implementation ) ) ); |
808 | |
809 | } |
810 | } |
811 | } |
812 | else if ( this.isLoggable( Level.WARNING ) ) |
813 | { |
814 | this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(), |
815 | Dependencies.class.getName() ), null ); |
816 | |
817 | } |
818 | |
819 | if ( decodedProperties != null ) |
820 | { |
821 | for ( Property decodedProperty : decodedProperties.getProperty() ) |
822 | { |
823 | final Property property = properties.getProperty( decodedProperty.getName() ); |
824 | |
825 | if ( property == null ) |
826 | { |
827 | report.getDetails().add( new ModelValidationReport.Detail( |
828 | "CLASS_MISSING_IMPLEMENTATION_PROPERTY", Level.SEVERE, getMessage( |
829 | "missingProperty", implementation.getIdentifier(), decodedProperty.getName() ), |
830 | new ObjectFactory().createImplementation( implementation ) ) ); |
831 | |
832 | } |
833 | else |
834 | { |
835 | if ( decodedProperty.getType() == null ? property.getType() != null |
836 | : !decodedProperty.getType().equals( property.getType() ) ) |
837 | { |
838 | report.getDetails().add( new ModelValidationReport.Detail( |
839 | "CLASS_ILLEGAL_IMPLEMENTATION_PROPERTY", Level.SEVERE, getMessage( |
840 | "illegalPropertyType", implementation.getIdentifier(), decodedProperty.getName(), |
841 | property.getType() == null ? "default" : property.getType(), |
842 | decodedProperty.getType() == null ? "default" : decodedProperty.getType() ), |
843 | new ObjectFactory().createImplementation( implementation ) ) ); |
844 | |
845 | } |
846 | } |
847 | } |
848 | } |
849 | else if ( this.isLoggable( Level.WARNING ) ) |
850 | { |
851 | this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(), |
852 | Properties.class.getName() ), null ); |
853 | |
854 | } |
855 | |
856 | if ( decodedMessages != null ) |
857 | { |
858 | for ( Message decodedMessage : decodedMessages.getMessage() ) |
859 | { |
860 | final Message message = messages.getMessage( decodedMessage.getName() ); |
861 | |
862 | if ( message == null ) |
863 | { |
864 | report.getDetails().add( new ModelValidationReport.Detail( |
865 | "CLASS_MISSING_IMPLEMENTATION_MESSAGE", Level.SEVERE, getMessage( |
866 | "missingMessage", implementation.getIdentifier(), decodedMessage.getName() ), |
867 | new ObjectFactory().createImplementation( implementation ) ) ); |
868 | |
869 | } |
870 | } |
871 | } |
872 | else if ( this.isLoggable( Level.WARNING ) ) |
873 | { |
874 | this.log( Level.WARNING, getMessage( "cannotValidateImplementation", |
875 | implementation.getIdentifier(), Messages.class.getName() ), null ); |
876 | |
877 | } |
878 | |
879 | if ( decodedSpecifications != null ) |
880 | { |
881 | for ( Specification decodedSpecification : decodedSpecifications.getSpecification() ) |
882 | { |
883 | final Specification specification = |
884 | this.getModules().getSpecification( decodedSpecification.getIdentifier() ); |
885 | |
886 | if ( specification == null ) |
887 | { |
888 | report.getDetails().add( new ModelValidationReport.Detail( |
889 | "CLASS_MISSING_SPECIFICATION", Level.SEVERE, getMessage( |
890 | "missingSpecification", implementation.getIdentifier(), |
891 | decodedSpecification.getIdentifier() ), |
892 | new ObjectFactory().createImplementation( implementation ) ) ); |
893 | |
894 | } |
895 | else |
896 | { |
897 | if ( decodedSpecification.getMultiplicity() != specification.getMultiplicity() ) |
898 | { |
899 | report.getDetails().add( new ModelValidationReport.Detail( |
900 | "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, getMessage( |
901 | "illegalMultiplicity", specification.getIdentifier(), |
902 | specification.getMultiplicity().value(), |
903 | decodedSpecification.getMultiplicity().value() ), |
904 | new ObjectFactory().createImplementation( implementation ) ) ); |
905 | |
906 | } |
907 | |
908 | if ( decodedSpecification.getScope() == null |
909 | ? specification.getScope() != null |
910 | : !decodedSpecification.getScope().equals( specification.getScope() ) ) |
911 | { |
912 | report.getDetails().add( new ModelValidationReport.Detail( |
913 | "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, getMessage( |
914 | "illegalScope", decodedSpecification.getIdentifier(), |
915 | specification.getScope() == null ? "Multiton" : specification.getScope(), |
916 | decodedSpecification.getScope() == null ? "Multiton" : decodedSpecification.getScope() ), |
917 | new ObjectFactory().createImplementation( implementation ) ) ); |
918 | |
919 | } |
920 | |
921 | if ( decodedSpecification.getClazz() == null ? specification.getClazz() != null |
922 | : !decodedSpecification.getClazz().equals( specification.getClazz() ) ) |
923 | { |
924 | report.getDetails().add( new ModelValidationReport.Detail( |
925 | "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, getMessage( |
926 | "illegalSpecificationClass", decodedSpecification.getIdentifier(), |
927 | specification.getClazz(), decodedSpecification.getClazz() ), |
928 | new ObjectFactory().createImplementation( implementation ) ) ); |
929 | |
930 | } |
931 | } |
932 | } |
933 | |
934 | for ( SpecificationReference decodedReference : decodedSpecifications.getReference() ) |
935 | { |
936 | final Specification specification = |
937 | specifications.getSpecification( decodedReference.getIdentifier() ); |
938 | |
939 | if ( specification == null ) |
940 | { |
941 | report.getDetails().add( new ModelValidationReport.Detail( |
942 | "CLASS_MISSING_SPECIFICATION", Level.SEVERE, getMessage( |
943 | "missingSpecification", implementation.getIdentifier(), decodedReference.getIdentifier() ), |
944 | new ObjectFactory().createImplementation( implementation ) ) ); |
945 | |
946 | } |
947 | else if ( decodedReference.getVersion() != null && specification.getVersion() != null && |
948 | VersionParser.compare( decodedReference.getVersion(), specification.getVersion() ) != 0 ) |
949 | { |
950 | final Module moduleOfSpecification = |
951 | this.getModules().getModuleOfSpecification( decodedReference.getIdentifier() ); |
952 | |
953 | final Module moduleOfImplementation = |
954 | this.getModules().getModuleOfImplementation( implementation.getIdentifier() ); |
955 | |
956 | report.getDetails().add( new ModelValidationReport.Detail( |
957 | "CLASS_INCOMPATIBLE_IMPLEMENTATION", Level.SEVERE, getMessage( |
958 | "incompatibleImplementation", javaClass.getClassName(), |
959 | moduleOfImplementation == null ? "<>" : moduleOfImplementation.getName(), |
960 | specification.getIdentifier(), |
961 | moduleOfSpecification == null ? "<>" : moduleOfSpecification.getName(), |
962 | decodedReference.getVersion(), specification.getVersion() ), |
963 | new ObjectFactory().createImplementation( implementation ) ) ); |
964 | |
965 | } |
966 | } |
967 | } |
968 | else if ( this.isLoggable( Level.WARNING ) ) |
969 | { |
970 | this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(), |
971 | Specifications.class.getName() ), null ); |
972 | |
973 | } |
974 | |
975 | return report; |
976 | } |
977 | catch ( final ParseException e ) |
978 | { |
979 | throw new ToolException( e ); |
980 | } |
981 | catch ( final TokenMgrError e ) |
982 | { |
983 | throw new ToolException( e ); |
984 | } |
985 | } |
986 | |
987 | /** |
988 | * Transforms committed meta-data of compiled Java classes of the modules of the instance. |
989 | * |
990 | * @param marshaller The marshaller to use for transforming classes. |
991 | * @param unmarshaller The unmarshaller to use for transforming classes. |
992 | * @param classesDirectory The directory holding the compiled class files. |
993 | * @param transformers The transformers to use for transforming the classes. |
994 | * |
995 | * @throws NullPointerException if {@code marshaller}, {@code unmarshaller}, {@code classesDirectory} or |
996 | * {@code transformers} is {@code null}. |
997 | * @throws ToolException if accessing class files fails. |
998 | * |
999 | * @see #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) |
1000 | */ |
1001 | public void transformClasses( final Marshaller marshaller, final Unmarshaller unmarshaller, |
1002 | final File classesDirectory, final List<Transformer> transformers ) |
1003 | throws ToolException |
1004 | { |
1005 | if ( marshaller == null ) |
1006 | { |
1007 | throw new NullPointerException( "marshaller" ); |
1008 | } |
1009 | if ( unmarshaller == null ) |
1010 | { |
1011 | throw new NullPointerException( "unmarshaller" ); |
1012 | } |
1013 | if ( transformers == null ) |
1014 | { |
1015 | throw new NullPointerException( "transformers" ); |
1016 | } |
1017 | if ( classesDirectory == null ) |
1018 | { |
1019 | throw new NullPointerException( "classesDirectory" ); |
1020 | } |
1021 | |
1022 | for ( Module m : this.getModules().getModule() ) |
1023 | { |
1024 | this.transformClasses( m, marshaller, unmarshaller, classesDirectory, transformers ); |
1025 | } |
1026 | } |
1027 | |
1028 | /** |
1029 | * Transforms committed meta-data of compiled Java classes of a given module of the modules of the instance. |
1030 | * |
1031 | * @param module The module to process. |
1032 | * @param marshaller The marshaller to use for transforming classes. |
1033 | * @param unmarshaller The unmarshaller to use for transforming classes. |
1034 | * @param classesDirectory The directory holding the compiled class files. |
1035 | * @param transformers The transformers to use for transforming the classes. |
1036 | * |
1037 | * @throws NullPointerException if {@code module}, {@code marshaller}, {@code unmarshaller}, |
1038 | * {@code classesDirectory} or {@code transformers} is {@code null}. |
1039 | * @throws ToolException if accessing class files fails. |
1040 | * |
1041 | * @see #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) |
1042 | * @see #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) |
1043 | */ |
1044 | public void transformClasses( final Module module, final Marshaller marshaller, final Unmarshaller unmarshaller, |
1045 | final File classesDirectory, final List<Transformer> transformers ) |
1046 | throws ToolException |
1047 | { |
1048 | if ( module == null ) |
1049 | { |
1050 | throw new NullPointerException( "module" ); |
1051 | } |
1052 | if ( marshaller == null ) |
1053 | { |
1054 | throw new NullPointerException( "marshaller" ); |
1055 | } |
1056 | if ( unmarshaller == null ) |
1057 | { |
1058 | throw new NullPointerException( "unmarshaller" ); |
1059 | } |
1060 | if ( transformers == null ) |
1061 | { |
1062 | throw new NullPointerException( "transformers" ); |
1063 | } |
1064 | if ( classesDirectory == null ) |
1065 | { |
1066 | throw new NullPointerException( "classesDirectory" ); |
1067 | } |
1068 | |
1069 | try |
1070 | { |
1071 | if ( module.getSpecifications() != null ) |
1072 | { |
1073 | for ( Specification s : module.getSpecifications().getSpecification() ) |
1074 | { |
1075 | if ( s.isClassDeclaration() ) |
1076 | { |
1077 | final String classLocation = s.getIdentifier().replace( '.', File.separatorChar ) + ".class"; |
1078 | final File classFile = new File( classesDirectory, classLocation ); |
1079 | |
1080 | if ( this.isLoggable( Level.INFO ) ) |
1081 | { |
1082 | this.log( Level.INFO, getMessage( "transforming", classFile.getAbsolutePath() ), null ); |
1083 | } |
1084 | |
1085 | final JavaClass javaClass = this.getJavaClass( classFile ); |
1086 | this.transformClasses( s, marshaller, unmarshaller, javaClass, transformers ); |
1087 | javaClass.dump( classFile ); |
1088 | } |
1089 | } |
1090 | } |
1091 | |
1092 | if ( module.getImplementations() != null ) |
1093 | { |
1094 | for ( Implementation i : module.getImplementations().getImplementation() ) |
1095 | { |
1096 | if ( i.isClassDeclaration() ) |
1097 | { |
1098 | final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class"; |
1099 | final File classFile = new File( classesDirectory, classLocation ); |
1100 | |
1101 | if ( this.isLoggable( Level.INFO ) ) |
1102 | { |
1103 | this.log( Level.INFO, getMessage( "transforming", classFile.getAbsolutePath() ), null ); |
1104 | } |
1105 | |
1106 | final JavaClass javaClass = this.getJavaClass( classFile ); |
1107 | this.transformClasses( i, marshaller, unmarshaller, javaClass, transformers ); |
1108 | javaClass.dump( classFile ); |
1109 | } |
1110 | } |
1111 | } |
1112 | } |
1113 | catch ( final IOException e ) |
1114 | { |
1115 | throw new ToolException( e ); |
1116 | } |
1117 | } |
1118 | |
1119 | /** |
1120 | * Transforms committed meta-data of compiled Java classes of a given specification of the modules of the instance. |
1121 | * |
1122 | * @param specification The specification to process. |
1123 | * @param marshaller The marshaller to use for transforming classes. |
1124 | * @param unmarshaller The unmarshaller to use for transforming classes. |
1125 | * @param javaClass The java class to process. |
1126 | * @param transformers The transformers to use for transforming the classes. |
1127 | * |
1128 | * @throws NullPointerException if {@code specification}, {@code marshaller}, {@code unmarshaller}, |
1129 | * {@code javaClass} or {@code transformers} is {@code null}. |
1130 | * @throws ToolException if accessing class files fails. |
1131 | * |
1132 | * @see org.jomc.model.ModelContext#createMarshaller() |
1133 | * @see org.jomc.model.ModelContext#createUnmarshaller() |
1134 | */ |
1135 | public void transformClasses( final Specification specification, final Marshaller marshaller, |
1136 | final Unmarshaller unmarshaller, final JavaClass javaClass, |
1137 | final List<Transformer> transformers ) throws ToolException |
1138 | { |
1139 | if ( specification == null ) |
1140 | { |
1141 | throw new NullPointerException( "specification" ); |
1142 | } |
1143 | if ( marshaller == null ) |
1144 | { |
1145 | throw new NullPointerException( "marshaller" ); |
1146 | } |
1147 | if ( unmarshaller == null ) |
1148 | { |
1149 | throw new NullPointerException( "unmarshaller" ); |
1150 | } |
1151 | if ( javaClass == null ) |
1152 | { |
1153 | throw new NullPointerException( "javaClass" ); |
1154 | } |
1155 | if ( transformers == null ) |
1156 | { |
1157 | throw new NullPointerException( "transformers" ); |
1158 | } |
1159 | |
1160 | try |
1161 | { |
1162 | Specification decodedSpecification = null; |
1163 | final ObjectFactory objectFactory = new ObjectFactory(); |
1164 | final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() ); |
1165 | if ( bytes != null ) |
1166 | { |
1167 | decodedSpecification = this.decodeModelObject( unmarshaller, bytes, Specification.class ); |
1168 | } |
1169 | |
1170 | if ( decodedSpecification != null ) |
1171 | { |
1172 | for ( Transformer transformer : transformers ) |
1173 | { |
1174 | final JAXBSource source = |
1175 | new JAXBSource( marshaller, objectFactory.createSpecification( decodedSpecification ) ); |
1176 | |
1177 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1178 | transformer.transform( source, result ); |
1179 | decodedSpecification = ( (JAXBElement<Specification>) result.getResult() ).getValue(); |
1180 | } |
1181 | |
1182 | this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject( |
1183 | marshaller, objectFactory.createSpecification( decodedSpecification ) ) ); |
1184 | |
1185 | } |
1186 | } |
1187 | catch ( final JAXBException e ) |
1188 | { |
1189 | throw new ToolException( e ); |
1190 | } |
1191 | catch ( final TransformerException e ) |
1192 | { |
1193 | throw new ToolException( e ); |
1194 | } |
1195 | } |
1196 | |
1197 | /** |
1198 | * Transforms committed meta-data of compiled Java classes of a given implementation of the modules of the instance. |
1199 | * |
1200 | * @param implementation The implementation 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 implementation}, {@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 Implementation implementation, final Marshaller marshaller, |
1214 | final Unmarshaller unmarshaller, final JavaClass javaClass, |
1215 | final List<Transformer> transformers ) throws ToolException |
1216 | { |
1217 | if ( implementation == null ) |
1218 | { |
1219 | throw new NullPointerException( "implementation" ); |
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 | Dependencies decodedDependencies = null; |
1241 | byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() ); |
1242 | if ( bytes != null ) |
1243 | { |
1244 | decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class ); |
1245 | } |
1246 | |
1247 | Messages decodedMessages = null; |
1248 | bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() ); |
1249 | if ( bytes != null ) |
1250 | { |
1251 | decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class ); |
1252 | } |
1253 | |
1254 | Properties decodedProperties = null; |
1255 | bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() ); |
1256 | if ( bytes != null ) |
1257 | { |
1258 | decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class ); |
1259 | } |
1260 | |
1261 | Specifications decodedSpecifications = null; |
1262 | bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() ); |
1263 | if ( bytes != null ) |
1264 | { |
1265 | decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class ); |
1266 | } |
1267 | |
1268 | final ObjectFactory of = new ObjectFactory(); |
1269 | for ( Transformer transformer : transformers ) |
1270 | { |
1271 | if ( decodedDependencies != null ) |
1272 | { |
1273 | final JAXBSource source = new JAXBSource( marshaller, of.createDependencies( decodedDependencies ) ); |
1274 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1275 | transformer.transform( source, result ); |
1276 | decodedDependencies = ( (JAXBElement<Dependencies>) result.getResult() ).getValue(); |
1277 | } |
1278 | |
1279 | if ( decodedMessages != null ) |
1280 | { |
1281 | final JAXBSource source = new JAXBSource( marshaller, of.createMessages( decodedMessages ) ); |
1282 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1283 | transformer.transform( source, result ); |
1284 | decodedMessages = ( (JAXBElement<Messages>) result.getResult() ).getValue(); |
1285 | } |
1286 | |
1287 | if ( decodedProperties != null ) |
1288 | { |
1289 | final JAXBSource source = new JAXBSource( marshaller, of.createProperties( decodedProperties ) ); |
1290 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1291 | transformer.transform( source, result ); |
1292 | decodedProperties = ( (JAXBElement<Properties>) result.getResult() ).getValue(); |
1293 | } |
1294 | |
1295 | if ( decodedSpecifications != null ) |
1296 | { |
1297 | final JAXBSource source = |
1298 | new JAXBSource( marshaller, of.createSpecifications( decodedSpecifications ) ); |
1299 | |
1300 | final JAXBResult result = new JAXBResult( unmarshaller ); |
1301 | transformer.transform( source, result ); |
1302 | decodedSpecifications = ( (JAXBElement<Specifications>) result.getResult() ).getValue(); |
1303 | } |
1304 | } |
1305 | |
1306 | if ( decodedDependencies != null ) |
1307 | { |
1308 | this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject( |
1309 | marshaller, of.createDependencies( decodedDependencies ) ) ); |
1310 | |
1311 | } |
1312 | |
1313 | if ( decodedMessages != null ) |
1314 | { |
1315 | this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject( |
1316 | marshaller, of.createMessages( decodedMessages ) ) ); |
1317 | |
1318 | } |
1319 | |
1320 | if ( decodedProperties != null ) |
1321 | { |
1322 | this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject( |
1323 | marshaller, of.createProperties( decodedProperties ) ) ); |
1324 | |
1325 | } |
1326 | |
1327 | if ( decodedSpecifications != null ) |
1328 | { |
1329 | this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject( |
1330 | marshaller, of.createSpecifications( decodedSpecifications ) ) ); |
1331 | |
1332 | } |
1333 | } |
1334 | catch ( final JAXBException e ) |
1335 | { |
1336 | throw new ToolException( e ); |
1337 | } |
1338 | catch ( final TransformerException e ) |
1339 | { |
1340 | throw new ToolException( e ); |
1341 | } |
1342 | } |
1343 | |
1344 | /** |
1345 | * Parses a class file. |
1346 | * |
1347 | * @param classFile The class file to parse. |
1348 | * |
1349 | * @return The parsed class file. |
1350 | * |
1351 | * @throws NullPointerException if {@code classFile} is {@code null}. |
1352 | * @throws ToolException if parsing {@code classFile} fails. |
1353 | * |
1354 | * @see JavaClass |
1355 | */ |
1356 | public JavaClass getJavaClass( final File classFile ) throws ToolException |
1357 | { |
1358 | if ( classFile == null ) |
1359 | { |
1360 | throw new NullPointerException( "classFile" ); |
1361 | } |
1362 | |
1363 | try |
1364 | { |
1365 | return this.getJavaClass( classFile.toURI().toURL(), classFile.getName() ); |
1366 | } |
1367 | catch ( final IOException e ) |
1368 | { |
1369 | throw new ToolException( e ); |
1370 | } |
1371 | } |
1372 | |
1373 | /** |
1374 | * Parses a class file. |
1375 | * |
1376 | * @param url The URL of the class file to parse. |
1377 | * @param className The name of the class at {@code url}. |
1378 | * |
1379 | * @return The parsed class file. |
1380 | * |
1381 | * @throws NullPointerException if {@code url} or {@code className} is {@code null}. |
1382 | * @throws ToolException if parsing fails. |
1383 | * |
1384 | * @see JavaClass |
1385 | */ |
1386 | public JavaClass getJavaClass( final URL url, final String className ) throws ToolException |
1387 | { |
1388 | if ( url == null ) |
1389 | { |
1390 | throw new NullPointerException( "url" ); |
1391 | } |
1392 | if ( className == null ) |
1393 | { |
1394 | throw new NullPointerException( "className" ); |
1395 | } |
1396 | |
1397 | try |
1398 | { |
1399 | return this.getJavaClass( url.openStream(), className ); |
1400 | } |
1401 | catch ( final IOException e ) |
1402 | { |
1403 | throw new ToolException( e ); |
1404 | } |
1405 | } |
1406 | |
1407 | /** |
1408 | * Parses a class file. |
1409 | * |
1410 | * @param stream The stream to read the class file from. |
1411 | * @param className The name of the class to read from {@code stream}. |
1412 | * |
1413 | * @return The parsed class file. |
1414 | * |
1415 | * @throws NullPointerException if {@code stream} or {@code className} is {@code null}. |
1416 | * @throws ToolException if parsing fails. |
1417 | * |
1418 | * @see JavaClass |
1419 | */ |
1420 | public JavaClass getJavaClass( final InputStream stream, final String className ) throws ToolException |
1421 | { |
1422 | if ( stream == null ) |
1423 | { |
1424 | throw new NullPointerException( "stream" ); |
1425 | } |
1426 | if ( className == null ) |
1427 | { |
1428 | throw new NullPointerException( "className" ); |
1429 | } |
1430 | |
1431 | try |
1432 | { |
1433 | final ClassParser parser = new ClassParser( stream, className ); |
1434 | final JavaClass clazz = parser.parse(); |
1435 | stream.close(); |
1436 | return clazz; |
1437 | } |
1438 | catch ( final IOException e ) |
1439 | { |
1440 | throw new ToolException( e ); |
1441 | } |
1442 | } |
1443 | |
1444 | /** |
1445 | * Gets an attribute from a java class. |
1446 | * |
1447 | * @param clazz The java class to get an attribute from. |
1448 | * @param attributeName The name of the attribute to get. |
1449 | * |
1450 | * @return The value of attribute {@code attributeName} of {@code clazz} or {@code null} if no such attribute |
1451 | * exists. |
1452 | * |
1453 | * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}. |
1454 | * @throws ToolException if getting the attribute fails. |
1455 | * |
1456 | * @see JavaClass#getAttributes() |
1457 | */ |
1458 | public byte[] getClassfileAttribute( final JavaClass clazz, final String attributeName ) throws ToolException |
1459 | { |
1460 | if ( clazz == null ) |
1461 | { |
1462 | throw new NullPointerException( "clazz" ); |
1463 | } |
1464 | if ( attributeName == null ) |
1465 | { |
1466 | throw new NullPointerException( "attributeName" ); |
1467 | } |
1468 | |
1469 | final Attribute[] attributes = clazz.getAttributes(); |
1470 | |
1471 | for ( int i = attributes.length - 1; i >= 0; i-- ) |
1472 | { |
1473 | final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() ); |
1474 | |
1475 | if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) ) |
1476 | { |
1477 | final Unknown unknown = (Unknown) attributes[i]; |
1478 | return unknown.getBytes(); |
1479 | } |
1480 | } |
1481 | |
1482 | return null; |
1483 | } |
1484 | |
1485 | /** |
1486 | * Adds or updates an attribute in a java class. |
1487 | * |
1488 | * @param clazz The class to update. |
1489 | * @param attributeName The name of the attribute to update. |
1490 | * @param data The new data of the attribute to update the {@code classFile} with. |
1491 | * |
1492 | * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}. |
1493 | * @throws ToolException if updating the class file fails. |
1494 | * |
1495 | * @see JavaClass#getAttributes() |
1496 | */ |
1497 | public void setClassfileAttribute( final JavaClass clazz, final String attributeName, final byte[] data ) |
1498 | throws ToolException |
1499 | { |
1500 | if ( clazz == null ) |
1501 | { |
1502 | throw new NullPointerException( "clazz" ); |
1503 | } |
1504 | if ( attributeName == null ) |
1505 | { |
1506 | throw new NullPointerException( "attributeName" ); |
1507 | } |
1508 | |
1509 | /* |
1510 | The JavaTM Virtual Machine Specification - Second Edition - Chapter 4.1 |
1511 | |
1512 | A Java virtual machine implementation is required to silently ignore any |
1513 | or all attributes in the attributes table of a ClassFile structure that |
1514 | it does not recognize. Attributes not defined in this specification are |
1515 | not allowed to affect the semantics of the class file, but only to |
1516 | provide additional descriptive information (§4.7.1). |
1517 | */ |
1518 | Attribute[] attributes = clazz.getAttributes(); |
1519 | |
1520 | int attributeIndex = -1; |
1521 | int nameIndex = -1; |
1522 | |
1523 | for ( int i = attributes.length - 1; i >= 0; i-- ) |
1524 | { |
1525 | final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() ); |
1526 | |
1527 | if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) ) |
1528 | { |
1529 | attributeIndex = i; |
1530 | nameIndex = attributes[i].getNameIndex(); |
1531 | } |
1532 | } |
1533 | |
1534 | if ( nameIndex == -1 ) |
1535 | { |
1536 | final Constant[] pool = clazz.getConstantPool().getConstantPool(); |
1537 | final Constant[] tmp = new Constant[ pool.length + 1 ]; |
1538 | System.arraycopy( pool, 0, tmp, 0, pool.length ); |
1539 | tmp[pool.length] = new ConstantUtf8( attributeName ); |
1540 | nameIndex = pool.length; |
1541 | clazz.setConstantPool( new ConstantPool( tmp ) ); |
1542 | } |
1543 | |
1544 | final Unknown unknown = new Unknown( nameIndex, data.length, data, clazz.getConstantPool() ); |
1545 | |
1546 | if ( attributeIndex == -1 ) |
1547 | { |
1548 | final Attribute[] tmp = new Attribute[ attributes.length + 1 ]; |
1549 | System.arraycopy( attributes, 0, tmp, 0, attributes.length ); |
1550 | tmp[attributes.length] = unknown; |
1551 | attributes = tmp; |
1552 | } |
1553 | else |
1554 | { |
1555 | attributes[attributeIndex] = unknown; |
1556 | } |
1557 | |
1558 | clazz.setAttributes( attributes ); |
1559 | } |
1560 | |
1561 | /** |
1562 | * Encodes a model object to a byte array. |
1563 | * |
1564 | * @param marshaller The marshaller to use for encoding the object. |
1565 | * @param modelObject The model object to encode. |
1566 | * |
1567 | * @return GZIP compressed XML document for {@code modelObject}. |
1568 | * |
1569 | * @throws NullPointerException if {@code marshaller} or {@code modelObject} is {@code null}. |
1570 | * @throws ToolException if encoding {@code modelObject} fails. |
1571 | */ |
1572 | public byte[] encodeModelObject( final Marshaller marshaller, final JAXBElement<? extends ModelObject> modelObject ) |
1573 | throws ToolException |
1574 | { |
1575 | if ( marshaller == null ) |
1576 | { |
1577 | throw new NullPointerException( "marshaller" ); |
1578 | } |
1579 | if ( modelObject == null ) |
1580 | { |
1581 | throw new NullPointerException( "modelObject" ); |
1582 | } |
1583 | |
1584 | try |
1585 | { |
1586 | final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
1587 | final GZIPOutputStream out = new GZIPOutputStream( baos ); |
1588 | marshaller.marshal( modelObject, out ); |
1589 | out.close(); |
1590 | return baos.toByteArray(); |
1591 | } |
1592 | catch ( final JAXBException e ) |
1593 | { |
1594 | throw new ToolException( e ); |
1595 | } |
1596 | catch ( final IOException e ) |
1597 | { |
1598 | throw new ToolException( e ); |
1599 | } |
1600 | } |
1601 | |
1602 | /** |
1603 | * Decodes a model object from a byte array. |
1604 | * |
1605 | * @param unmarshaller The unmarshaller to use for decoding the object. |
1606 | * @param bytes The encoded model object to decode. |
1607 | * @param type The type of the encoded model object. |
1608 | * @param <T> The type of the decoded model object. |
1609 | * |
1610 | * @return Model object decoded from {@code bytes}. |
1611 | * |
1612 | * @throws NullPointerException if {@code unmarshaller}, {@code bytes} or {@code type} is {@code null}. |
1613 | * @throws ToolException if decoding {@code bytes} fails. |
1614 | */ |
1615 | public <T extends ModelObject> T decodeModelObject( final Unmarshaller unmarshaller, final byte[] bytes, |
1616 | final Class<T> type ) throws ToolException |
1617 | { |
1618 | if ( unmarshaller == null ) |
1619 | { |
1620 | throw new NullPointerException( "unmarshaller" ); |
1621 | } |
1622 | if ( bytes == null ) |
1623 | { |
1624 | throw new NullPointerException( "bytes" ); |
1625 | } |
1626 | if ( type == null ) |
1627 | { |
1628 | throw new NullPointerException( "type" ); |
1629 | } |
1630 | |
1631 | try |
1632 | { |
1633 | final ByteArrayInputStream bais = new ByteArrayInputStream( bytes ); |
1634 | final GZIPInputStream in = new GZIPInputStream( bais ); |
1635 | final JAXBElement<T> element = (JAXBElement<T>) unmarshaller.unmarshal( in ); |
1636 | in.close(); |
1637 | return element.getValue(); |
1638 | } |
1639 | catch ( final JAXBException e ) |
1640 | { |
1641 | throw new ToolException( e ); |
1642 | } |
1643 | catch ( final IOException e ) |
1644 | { |
1645 | throw new ToolException( e ); |
1646 | } |
1647 | } |
1648 | |
1649 | private static String getMessage( final String key, final Object... arguments ) |
1650 | { |
1651 | if ( key == null ) |
1652 | { |
1653 | throw new NullPointerException( "key" ); |
1654 | } |
1655 | |
1656 | return MessageFormat.format( ResourceBundle.getBundle( JavaClasses.class.getName().replace( '.', '/' ) ). |
1657 | getString( key ), arguments ); |
1658 | |
1659 | } |
1660 | |
1661 | } |