View Javadoc

1   /*
2    *  jDTAUS Core Container Mojo
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.container.mojo;
22  
23  import java.io.ByteArrayOutputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.io.StringWriter;
28  import java.util.Locale;
29  import java.util.zip.GZIPOutputStream;
30  import javax.xml.bind.JAXBException;
31  import org.apache.bcel.classfile.Attribute;
32  import org.apache.bcel.classfile.ClassParser;
33  import org.apache.bcel.classfile.Constant;
34  import org.apache.bcel.classfile.ConstantPool;
35  import org.apache.bcel.classfile.ConstantUtf8;
36  import org.apache.bcel.classfile.JavaClass;
37  import org.apache.bcel.classfile.Unknown;
38  import org.apache.maven.plugin.MojoExecutionException;
39  import org.apache.maven.plugin.MojoFailureException;
40  import org.jdtaus.core.container.ContainerError;
41  import org.jdtaus.core.container.ContextError;
42  import org.jdtaus.core.container.Implementation;
43  import org.jdtaus.core.container.Model;
44  import org.jdtaus.core.container.ModelError;
45  import org.jdtaus.core.container.ModelFactory;
46  import org.jdtaus.core.container.Module;
47  import org.jdtaus.core.container.Specification;
48  
49  /**
50   * Mojo to commit container meta-data to compiled java classes.
51   *
52   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
53   * @version $JDTAUS: JavaCommitMojo.java 8743 2012-10-07 03:06:20Z schulte $
54   * @goal java-commit
55   * @phase process-classes
56   * @requiresDependencyResolution test
57   */
58  public class JavaCommitMojo extends JavaContainerMojo
59  {
60  
61      /** Creates a new {@code JavaCommitMojo} instance. */
62      public JavaCommitMojo()
63      {
64          super();
65      }
66  
67      public void execute() throws MojoExecutionException, MojoFailureException
68      {
69          final ClassLoader mavenLoader = Thread.currentThread().
70              getContextClassLoader();
71  
72          try
73          {
74              Thread.currentThread().setContextClassLoader(
75                  this.getRuntimeClassLoader( mavenLoader ) );
76  
77              enableThreadContextClassLoader();
78  
79              final Module module = this.getModule();
80              final Model model = ModelFactory.newModel();
81  
82              if ( module != null )
83              {
84                  for ( int i = module.getSpecifications().size() - 1; i >= 0;
85                        i-- )
86                  {
87                      final Specification spec = module.getSpecifications().
88                          getSpecification( i );
89  
90                      this.commitSpecification(
91                          spec, new File( this.getMavenProject().getBuild().
92                          getOutputDirectory() ) );
93  
94                  }
95  
96                  for ( int i = module.getImplementations().size() - 1; i >= 0;
97                        i-- )
98                  {
99                      final Implementation impl = module.getImplementations().
100                         getImplementation( i );
101 
102                     this.commitImplementation(
103                         model, impl, new File( this.getMavenProject().
104                         getBuild().getOutputDirectory() ) );
105 
106                 }
107             }
108         }
109         catch ( final ContextError e )
110         {
111             throw new MojoExecutionException( e.getMessage(), e );
112         }
113         catch ( final ContainerError e )
114         {
115             throw new MojoExecutionException( e.getMessage(), e );
116         }
117         catch ( final ModelError e )
118         {
119             throw new MojoExecutionException( e.getMessage(), e );
120         }
121         catch ( final Exception e )
122         {
123             throw new MojoExecutionException( e.getMessage(), e );
124         }
125         finally
126         {
127             disableThreadContextClassLoader();
128             Thread.currentThread().setContextClassLoader( mavenLoader );
129         }
130 
131     }
132 
133     protected void commitImplementation( final Model model,
134                                          final Implementation impl,
135                                          final File outputDirectory )
136         throws IOException, JAXBException
137     {
138         this.enhanceClass( outputDirectory, impl.getIdentifier(),
139                            Module.class.getName(),
140                            this.serializeImplementation( model, impl ) );
141 
142     }
143 
144     protected void commitSpecification( final Specification spec,
145                                         final File outputDirectory )
146         throws IOException, JAXBException
147     {
148         this.enhanceClass( outputDirectory, spec.getIdentifier(),
149                            Specification.class.getName(),
150                            this.serializeSpecification( spec ) );
151 
152     }
153 
154     private byte[] serializeImplementation( final Model model,
155                                             final Implementation impl )
156         throws IOException, JAXBException
157     {
158         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
159         final GZIPOutputStream out = new GZIPOutputStream( baos );
160 
161         this.getModelManager().getContainerMarshaller().marshal(
162             this.getModelManager().getResolvedImplementation( model, impl ),
163             out );
164 
165         out.close();
166 
167         if ( this.getLog().isDebugEnabled() )
168         {
169             final StringWriter writer = new StringWriter();
170             this.getModelManager().getContainerMarshaller().marshal(
171                 this.getModelManager().getResolvedImplementation(
172                 model, impl ), writer );
173 
174             this.getLog().debug( writer.toString() );
175         }
176 
177         return baos.toByteArray();
178     }
179 
180     private byte[] serializeSpecification( final Specification spec )
181         throws IOException, JAXBException
182     {
183         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
184         final GZIPOutputStream out = new GZIPOutputStream( baos );
185 
186         this.getModelManager().getContainerMarshaller().marshal(
187             this.getModelManager().getSpecification( spec ), out );
188 
189         out.close();
190 
191         if ( this.getLog().isDebugEnabled() )
192         {
193             final StringWriter writer = new StringWriter();
194             this.getModelManager().getContainerMarshaller().marshal(
195                 this.getModelManager().getSpecification( spec ), writer );
196 
197             this.getLog().debug( writer.toString() );
198         }
199 
200         return baos.toByteArray();
201     }
202 
203     private void enhanceClass( final File outputDirectory,
204                                final String identifier,
205                                final String attributeName,
206                                final byte[] meta )
207         throws IOException
208     {
209         /*
210         The JavaTM Virtual Machine Specification - Second Edition - Chapter 4.1
211 
212         A Java virtual machine implementation is required to silently ignore any
213         or all attributes in the attributes table of a ClassFile structure that
214         it does not recognize. Attributes not defined in this specification are
215         not allowed to affect the semantics of the class file, but only to
216         provide additional descriptive information (ยง4.7.1).
217          */
218 
219         final String classLocation =
220             identifier.replace( '.', File.separatorChar ) + ".class";
221 
222         final File classFile = new File( outputDirectory, classLocation );
223         final long fileLength = classFile.length();
224         final ClassParser parser =
225             new ClassParser( new FileInputStream( classFile ),
226                              classFile.getName() );
227 
228         final JavaClass clazz = parser.parse();
229         Attribute[] attributes = clazz.getAttributes();
230 
231         int attributeIndex = -1;
232         int nameIndex = -1;
233 
234         for ( int i = attributes.length - 1; i >= 0; i-- )
235         {
236             final Constant constant = clazz.getConstantPool().
237                 getConstant( attributes[i].getNameIndex() );
238 
239             if ( constant instanceof ConstantUtf8 )
240             {
241                 if ( attributeName.equals(
242                     ( (ConstantUtf8) constant ).getBytes() ) )
243                 {
244                     attributeIndex = i;
245                     nameIndex = attributes[i].getNameIndex();
246                 }
247             }
248         }
249 
250         if ( nameIndex == -1 )
251         {
252             final Constant[] pool = clazz.getConstantPool().getConstantPool();
253             final Constant[] tmp = new Constant[ pool.length + 1 ];
254             System.arraycopy( pool, 0, tmp, 0, pool.length );
255             tmp[pool.length] = new ConstantUtf8( attributeName );
256             nameIndex = pool.length;
257             clazz.setConstantPool( new ConstantPool( tmp ) );
258         }
259 
260         final Unknown unknown = new Unknown( nameIndex, meta.length, meta,
261                                              clazz.getConstantPool() );
262 
263         if ( attributeIndex == -1 )
264         {
265             final Attribute[] tmp = new Attribute[ attributes.length + 1 ];
266             System.arraycopy( attributes, 0, tmp, 0, attributes.length );
267             tmp[attributes.length] = unknown;
268             attributeIndex = attributes.length;
269             attributes = tmp;
270         }
271         else
272         {
273             attributes[attributeIndex] = unknown;
274         }
275 
276         clazz.setAttributes( attributes );
277         clazz.dump( classFile );
278 
279         final Number bytes = new Long( classFile.length() - fileLength );
280 
281         this.getLog().info( JavaContainerMojoBundle.getInstance().
282             getCommittedFileMessage( Locale.getDefault(), identifier, bytes ) );
283 
284     }
285 
286 }