View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.tools.schema;
21  
22  
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileWriter;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.InputStreamReader;
29  import java.io.Reader;
30  import java.io.Writer;
31  import java.util.ArrayList;
32  import java.util.List;
33  
34  import org.apache.directory.server.schema.bootstrap.AbstractBootstrapSchema;
35  import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
36  import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
37  import org.apache.directory.shared.ldap.constants.SchemaConstants;
38  import org.apache.directory.shared.ldap.schema.parser.AttributeTypeLiteral;
39  import org.apache.directory.shared.ldap.schema.parser.ObjectClassLiteral;
40  import org.apache.directory.shared.ldap.schema.parser.OpenLdapSchemaParser;
41  import org.apache.maven.plugin.AbstractMojo;
42  import org.apache.maven.plugin.MojoExecutionException;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.velocity.VelocityContext;
45  import org.apache.velocity.app.Velocity;
46  
47  
48  /**
49   * Maven 2 plugin mojo wrapper for directory plugin.
50   * 
51   * @goal generate
52   * @description Generates ApacheDS schema classes from OpenLDAP schema files
53   * @phase generate-sources
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev: 638228 $
56   */
57  public class DirectorySchemaToolMojo extends AbstractMojo
58  {
59      /**
60       * The directory containing the OpenLDAP schema files.
61       * @parameter expression="src/main/schema"
62       */
63      private File sourceDirectory;
64  
65      /**
66       * The target directory into which the plugin generates schema java sources.
67       * @parameter expression="target/generated-sources"
68       */
69      private File outputDirectory;
70  
71      /**
72       * The default package to use for generated schema classes.
73       * @parameter expression="org.apache.directory.server.schema.bootstrap"
74       */
75      private String defaultPackage;
76  
77      /**
78       * The distinguished name of the default schema owner.
79       * @parameter expression="uid=admin,ou=system"
80       */
81      private String defaultOwner;
82  
83      /**
84       * The set of schemas to generate classes for.
85       * @parameter 
86       */
87      private Schema[] schemas;
88  
89      /**
90       * Toggles verbose output.
91       * @parameter expression="true"
92       */
93      private boolean verboseOutput;
94  
95      /**
96       * @parameter expression="${project}"
97       * @required
98       */
99      private MavenProject project;
100 
101 
102     public DirectorySchemaToolMojo() throws Exception
103     {
104         Velocity.init();
105     }
106 
107 
108     private void generate( BootstrapSchema schema ) throws Exception
109     {
110         if ( schema == null )
111         {
112             throw new NullPointerException( "the schema property must be set" );
113         }
114 
115         String filePath = sourceDirectory + File.separator + schema.getSchemaName() + ".schema";
116         InputStream in = new FileInputStream( filePath );
117         OpenLdapSchemaParser parser = new OpenLdapSchemaParser();
118         parser.parse( in );
119         generateSchema( schema );
120         generateAttributeTypes( parser, schema );
121         generateObjectClasses( parser, schema );
122         generateRest( schema );
123     }
124 
125 
126     protected void generateSchema( BootstrapSchema schema ) throws Exception
127     {
128         StringBuffer schemaCapped = new StringBuffer();
129         schemaCapped.append( Character.toUpperCase( schema.getSchemaName().charAt( 0 ) ) );
130         schemaCapped.append( schema.getSchemaName().substring( 1, schema.getSchemaName().length() ) );
131 
132         VelocityContext context = new VelocityContext();
133         context.put( "package", schema.getPackageName() );
134         context.put( "classname", schemaCapped.toString() + "Schema" );
135         context.put( "schema", schema.getSchemaName() );
136         context.put( "owner", schema.getOwner() );
137         context.put( "deps", schema.getDependencies() );
138 
139         Reader fileIn = getResourceReader( "Schema.template" );
140         Writer writer = getResourceWriter( schema.getPackageName(), schema.getUnqualifiedClassName() );
141         Velocity.evaluate( context, writer, "LOG", fileIn );
142 
143         writer.flush();
144         writer.close();
145     }
146 
147 
148     protected void generateRest( BootstrapSchema schema ) throws Exception
149     {
150         List types = new ArrayList();
151         types.addAll( ProducerTypeEnum.getList() );
152         types.remove( ProducerTypeEnum.ATTRIBUTE_TYPE_PRODUCER );
153         types.remove( ProducerTypeEnum.OBJECT_CLASS_PRODUCER );
154 
155         ProducerTypeEnum type = null;
156         for ( int ii = 0; ii < types.size(); ii++ )
157         {
158             type = ( ProducerTypeEnum ) types.get( ii );
159 
160             if ( exists( schema.getFullDefaultBaseClassName( type ), type ) )
161             {
162                 continue;
163             }
164 
165             VelocityContext context = new VelocityContext();
166             context.put( "package", schema.getPackageName() );
167             context.put( "classname", schema.getUnqualifiedClassName( type ) );
168             context.put( "schema", schema.getSchemaName() );
169             context.put( "owner", schema.getOwner() );
170             context.put( "type", type.getName().substring( 0, type.getName().length() - 8 ) );
171 
172             String typeName = null;
173             
174             switch ( type )
175             {
176                 case COMPARATOR_PRODUCER :
177                     typeName = "ProducerTypeEnum.COMPARATOR_PRODUCER";
178                     break;
179                     
180                 case DIT_CONTENT_RULE_PRODUCER :
181                     typeName = "ProducerTypeEnum.DIT_CONTENT_RULE_PRODUCER";
182                     break;
183                     
184                 case DIT_STRUCTURE_RULE_PRODUCER :
185                     typeName = "ProducerTypeEnum.DIT_STRUCTURE_RULE_PRODUCER";
186                     break;
187                     
188                 case MATCHING_RULE_PRODUCER :
189                     typeName = "ProducerTypeEnum.MATCHING_RULE_PRODUCER";
190                     break;
191                     
192                 case MATCHING_RULE_USE_PRODUCER :
193                     typeName = "ProducerTypeEnum.MATCHING_RULE_USE_PRODUCER";
194                     break;
195                     
196                 case NAME_FORM_PRODUCER :
197                     typeName = "ProducerTypeEnum.NAME_FORM_PRODUCER";
198                     break;
199                     
200                 case NORMALIZER_PRODUCER :
201                     typeName = "ProducerTypeEnum.NORMALIZER_PRODUCER";
202                     break;
203                     
204                 case SYNTAX_CHECKER_PRODUCER :
205                     typeName = "ProducerTypeEnum.SYNTAX_CHECKER_PRODUCER";
206                     break;
207                     
208                 case SYNTAX_PRODUCER :
209                     typeName = "ProducerTypeEnum.SYNTAX_PRODUCER";
210                     break;
211                     
212                 default:
213                     throw new IllegalStateException( "Unexpected producer: " + type.getName() );
214             }
215 
216             context.put( "typeName", typeName );
217             runVelocity( schema.getPackageName(), schema.getUnqualifiedClassName( type ), context, "typeless.template",
218                 type );
219         }
220     }
221 
222 
223     protected void generateAttributeTypes( OpenLdapSchemaParser parser, BootstrapSchema schema ) throws Exception
224     {
225         final ProducerTypeEnum type = ProducerTypeEnum.ATTRIBUTE_TYPE_PRODUCER;
226 
227         // check to see if the producer exists for this type
228         if ( exists( schema.getFullDefaultBaseClassName( type ), type ) )
229         {
230             return;
231         }
232 
233         int size = parser.getAttributeTypes().size();
234         AttributeTypeLiteral[] attributeTypes = new AttributeTypeLiteral[size];
235         attributeTypes = ( AttributeTypeLiteral[] ) parser.getAttributeTypes().toArray( attributeTypes );
236 
237         VelocityContext context = new VelocityContext();
238         context.put( "package", schema.getPackageName() );
239         context.put( "classname", schema.getUnqualifiedClassName( type ) );
240         context.put( "schema", schema.getSchemaName() );
241         context.put( "owner", schema.getOwner() );
242         context.put( "schemaDepCount", new Integer( schema.getDependencies().length ) );
243         context.put( "schemaDeps", new String[]
244             { "dep1", "dep2" } );
245         context.put( "attrTypes", attributeTypes );
246         runVelocity( schema.getPackageName(), schema.getUnqualifiedClassName( type ), context,
247             "AttributeTypes.template", type );
248     }
249 
250 
251     protected void generateObjectClasses( OpenLdapSchemaParser parser, BootstrapSchema schema ) throws Exception
252     {
253         final ProducerTypeEnum type = ProducerTypeEnum.OBJECT_CLASS_PRODUCER;
254 
255         // check to see if the producer exists for this type
256         if ( exists( schema.getFullDefaultBaseClassName( type ), type ) )
257         {
258             return;
259         }
260 
261         int size = parser.getObjectClassTypes().size();
262         ObjectClassLiteral[] objectClasses = new ObjectClassLiteral[size];
263         objectClasses = ( ObjectClassLiteral[] ) parser.getObjectClassTypes().toArray( objectClasses );
264 
265         VelocityContext context = new VelocityContext();
266         context.put( "package", schema.getPackageName() );
267         context.put( "classname", schema.getUnqualifiedClassName( type ) );
268         context.put( "schema", schema.getSchemaName() );
269         context.put( "owner", schema.getOwner() );
270         context.put( "schemaDepCount", new Integer( schema.getDependencies().length ) );
271         context.put( "schemaDeps", new String[]
272             { "dep1", "dep2" } );
273         context.put( SchemaConstants.OBJECT_CLASSES_AT, objectClasses );
274         runVelocity( schema.getPackageName(), schema.getUnqualifiedClassName( type ), context,
275             "ObjectClasses.template", type );
276     }
277 
278 
279     protected void runVelocity( String pkg, String uqcn, VelocityContext context, String template, ProducerTypeEnum type )
280         throws Exception
281     {
282         Reader fileIn = getResourceReader( template );
283         Writer writer = getResourceWriter( pkg, uqcn );
284         Velocity.evaluate( context, writer, "LOG", fileIn );
285         writer.flush();
286         writer.close();
287     }
288 
289 
290     protected Reader getResourceReader( String res ) throws IOException
291     {
292         return new InputStreamReader( getClass().getResourceAsStream( res ) );
293     }
294 
295 
296     protected boolean mkdirs( String base, String path )
297     {
298         String[] comps = path.split( "/" );
299         File file = new File( base );
300 
301         if ( !file.exists() )
302         {
303             file.mkdirs();
304         }
305 
306         for ( int ii = 0; ii < comps.length; ii++ )
307         {
308             file = new File( file, comps[ii] );
309 
310             if ( !file.exists() )
311             {
312                 file.mkdirs();
313             }
314         }
315 
316         return file.exists();
317     }
318 
319 
320     protected FileWriter getResourceWriter( String pkg, String classname ) throws IOException
321     {
322         mkdirs( outputDirectory.getPath(), pkg.replace( '.', File.separatorChar ) );
323         File base = outputDirectory;
324         String relativePath = pkg.replace( '.', File.separatorChar );
325         File dir = new File( base, relativePath );
326         return new FileWriter( new File( dir, classname + ".java" ) );
327     }
328 
329 
330     protected boolean exists( String defaultClass, ProducerTypeEnum type )
331     {
332         // check to see if any of the classes are available in the java 
333         // source directory, if so we return true
334         File defaultFile = new File( project.getBuild().getSourceDirectory() + File.separator
335             + getFilePath( defaultClass ) );
336         return defaultFile.exists();
337     }
338 
339 
340     private String getFilePath( String fqcn )
341     {
342         String path = fqcn.replace( '.', File.separatorChar );
343         path += ".java";
344         return path;
345     }
346 
347 
348     private boolean isStale( BootstrapSchema schema )
349     {
350         String pkgPath = schema.getPackageName().replace( '.', File.separatorChar );
351         File dir = new File( outputDirectory, pkgPath );
352         File schemaClassFile = new File( dir, schema.getUnqualifiedClassName() + ".java" );
353 
354         if ( !schemaClassFile.exists() )
355         {
356             return true;
357         }
358 
359         File schemaFile = new File( sourceDirectory, schema.getSchemaName() + ".schema" );
360         return schemaFile.lastModified() > schemaClassFile.lastModified();
361     }
362 
363 
364     public void execute() throws MojoExecutionException
365     {
366         // Bypass if no schemas have yet been defined 
367         if ( schemas == null || schemas.length == 0 )
368         {
369             getLog().warn( "No schemas defined for directory plugin!" );
370             return;
371         }
372 
373         // Make sure schema configurations have a name field and set defaults
374         // for any other missing properties of the bean: pkg and owner.
375         for ( int ii = 0; ii < schemas.length; ii++ )
376         {
377             Schema schema = schemas[ii];
378 
379             if ( schema.getName() == null )
380             {
381                 String msg = ii + "th schema configuration element must specify a name.";
382                 getLog().error( msg );
383                 throw new MojoExecutionException( msg );
384             }
385 
386             if ( schema.getPkg() == null )
387             {
388                 schema.setPkg( defaultPackage );
389             }
390 
391             if ( schema.getOwner() == null )
392             {
393                 schema.setOwner( defaultOwner );
394             }
395         }
396 
397         // Report configuration if verbose output is enabled
398         if ( verboseOutput )
399         {
400             report();
401         }
402 
403         // Create output directory if it does not exist
404         if ( !outputDirectory.exists() )
405         {
406             outputDirectory.mkdirs();
407         }
408 
409         // Generate for each schema 
410         for ( int ii = 0; ii < schemas.length; ii++ )
411         {
412             try
413             {
414                 BootstrapSchema bootstrapSchema = new AbstractBootstrapSchema( schemas[ii].getOwner(), schemas[ii]
415                     .getName(), schemas[ii].getPkg(), schemas[ii].getDependencies() )
416                 {
417                 };
418 
419                 if ( isStale( bootstrapSchema ) )
420                 {
421                     getLog().info( "Generating " + schemas[ii].getName() + " schema." );
422                     generate( bootstrapSchema );
423                 }
424                 else
425                 {
426                     getLog().info( schemas[ii].getName() + " schema is up to date." );
427                 }
428             }
429             catch ( Exception e )
430             {
431                 throw new MojoExecutionException( "Failed while generating sources for " + schemas[ii].getName(), e );
432             }
433         }
434 
435         project.addCompileSourceRoot( outputDirectory.getPath() );
436     }
437 
438 
439     private void report()
440     {
441         getLog().info( "===================================================================" );
442         getLog().info( "[directory:generate]" );
443         getLog().info( "sourceDirectory = " + sourceDirectory );
444         getLog().info( "outputDirectory = " + outputDirectory );
445         getLog().info( "defaultPackage  = " + defaultPackage );
446         getLog().info( "defaultOwner    = " + defaultOwner );
447         getLog().info( "----------------------------- schemas -----------------------------" );
448 
449         if ( schemas != null )
450         {
451             for ( int ii = 0; ii < schemas.length; ii++ )
452             {
453                 getLog().info( "SCHEMA: " + schemas[ii].getName() );
454 
455                 if ( schemas[ii].getDependencies() != null )
456                 {
457                     StringBuffer buf = new StringBuffer();
458                     for ( int jj = 0; jj < schemas[ii].getDependencies().length; jj++ )
459                     {
460                         buf.append( schemas[ii].getDependencies()[jj] );
461                         buf.append( " " );
462                     }
463                     getLog().info( "DEPENDENCIES: " + buf.toString() );
464                 }
465 
466                 getLog().info( "PACKAGE: " + schemas[ii].getPkg() );
467                 getLog().info( "OWNER: " + schemas[ii].getOwner() );
468 
469                 if ( ii + 1 < schemas.length )
470                 {
471                     getLog().info( "" );
472                 }
473             }
474         }
475 
476         getLog().info( "===================================================================" );
477     }
478 }