View Javadoc

1   package org.openphacts.nextprot;
2   
3   /**
4    * <code>TemplateAssembler</code> assembles the templates in a given directory 
5    * and produces an aggregate document.
6    *
7    * @author <a href="mailto:Oliver.Karch@merck.de>Oliver Karch</a>
8    *
9    */
10  import java.io.File;
11  import java.io.FileWriter;
12  import java.io.InputStream;
13  import java.io.IOException;
14  import java.io.Writer;
15  
16  import java.net.URL;
17  
18  import java.sql.SQLException;
19  
20  import java.util.Collection;
21  import java.util.Iterator;
22  import java.util.Map;
23  import java.util.Properties;
24  
25  import org.apache.commons.cli.Option;
26  import org.apache.commons.cli.Options;
27  import org.apache.commons.cli.OptionBuilder;
28  import org.apache.commons.cli.ParseException;
29  import org.apache.commons.cli.PosixParser;
30  import org.apache.commons.cli.CommandLine;
31  import org.apache.commons.cli.CommandLineParser;
32  import org.apache.commons.cli.HelpFormatter;
33  
34  import org.apache.commons.io.FileUtils;
35  // import org.apache.commons.io.IOUtils;
36  
37  import org.apache.commons.lang.StringUtils;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  
42  import org.apache.velocity.VelocityContext;
43  import org.apache.velocity.context.Context;
44  import org.apache.velocity.app.VelocityEngine;
45  import org.apache.velocity.runtime.RuntimeConstants;
46  
47  import org.apache.velocity.tools.ToolManager;
48  import org.apache.velocity.tools.ToolContext;
49  import org.apache.velocity.tools.config.XmlFactoryConfiguration;
50  
51  import velosurf.Velosurf;
52  
53  public class TemplateAssembler {
54      private ToolManager    toolManager;
55      private VelocityEngine vEngine;
56  
57      private static Log log = LogFactory.getLog(TemplateAssembler.class);
58  
59      private static final String LOGGER_NAME      = "CONSOLE";
60      private static final String MAIN_TEMPLATE    = "main.vm";
61      private static final String TOOLS_CONFIG     = "tools.xml";
62      private static final String DATABASE_CONFIG  = "database.xml";
63      private static final String VELOSURF_KEY     = "db";
64  
65      private static final String OPT_OUTPUT       = "output";
66      private static final String OPT_VELOSURF     = "velosurf";
67      private static final String OPT_USER         = "user";
68      private static final String OPT_PASSWORD     = "password";
69      private static final String OPT_MODEL        = "dbmodel";
70  
71      private static final String USAGE = "nextor [<template-directory>|<template-file>] [<options>]";
72  
73      public TemplateAssembler() {
74      }
75  
76      private ToolManager getToolManager() {
77  	if( toolManager == null ) {
78  	    toolManager = new ToolManager( false, true );
79  	    try {
80  		InputStream is = this.getClass().getClassLoader().getResourceAsStream( TOOLS_CONFIG );
81  		if( is != null ) {
82  		    XmlFactoryConfiguration cfg = new XmlFactoryConfiguration();
83  		    cfg.read( is );
84  		    is.close();
85  		    toolManager.configure( cfg );
86  		    log.debug( "Configured context tools from "+TOOLS_CONFIG );
87  		}
88  		else
89  		    log.warn( "Cannot configure tools" );
90  	    }
91  	    catch( IOException ioe ) {
92  		log.warn( "Cannot configure tools: "+((ioe.getMessage()!=null)?ioe.getMessage():"") );
93  	    }
94  	}
95  	return toolManager;
96      }
97  
98      private void insertVelosurf( File templDir, String mPath, Context ctxt ) {
99  	File tDir = templDir.getAbsoluteFile();
100 	if( templDir.isFile() ) 
101 	    tDir = templDir.getParentFile();
102 	if( tDir == null ) {
103 	    log.warn( "Cannot inject Velosurf tool into velocity context" );
104 	    return;
105 	}
106 
107 	File modelFile = null;
108 	if( mPath != null ) 
109 	    modelFile = new File( mPath );
110 	else
111 	    modelFile = new File( tDir, DATABASE_CONFIG );
112 	if( !modelFile.exists() ) {
113 	    log.warn( "Cannot find database model configuration at "+modelFile );
114 	    return;
115 	}
116 	log.debug( "Reading database model from "+modelFile );
117 	try {
118 	    Velosurf velo = new Velosurf( modelFile );
119 	    String kName = (String)ctxt.get( OPT_VELOSURF );
120 	    if( kName == null )
121 		kName = VELOSURF_KEY;
122 	    ctxt.put( kName, velo );
123 	    log.debug( "Velosurf injected into context, access name is \""+kName+"\"" );
124 	}
125 	catch( IOException ioe ) {
126 	    log.warn( ioe );
127 	}
128 	catch( SQLException sqe ) {
129 	    log.warn( sqe );
130 	}
131     }
132 
133     private void insertTemplateContent( File templDir, Context ctxt ) {
134 	File tDir = templDir;
135 	if( templDir.isFile() )
136 	    tDir = templDir.getParentFile();
137 
138 	Iterator<File> it = FileUtils.iterateFiles( tDir, new String[] { "xml", "vm", "vtl" }, false );
139 	while( it.hasNext() ) {
140 	    File theF = it.next();
141 	    try {
142 		String cont = FileUtils.readFileToString( theF );
143 		String fKey = theF.getName().replace( '.', '_' );
144 		ctxt.put( fKey, cont );
145 		log.debug( "Load content of \""+fKey+"\" from "+theF );
146 	    }
147 	    catch( IOException ioe ) {
148 		log.warn( ioe );
149 	    }
150 	}
151     }
152 
153     private Context createVelocityContext( File templDir, Map context ) {
154 	ToolManager manager = getToolManager();
155 	if( manager == null )
156 	    return new VelocityContext( context );
157 	ToolContext tc = manager.createContext();
158 
159 	// insert the velosurf tool
160 	insertVelosurf( templDir, (String)context.get( OPT_MODEL ), tc );
161 
162 	// inject the template content for later manipulation
163 	insertTemplateContent( templDir, tc );
164 
165 	// add additional tools, e.g. FileUtils
166 	tc.put( "files", FileUtils.class );
167 	tc.put( "strings", StringUtils.class );
168 
169 	// add additional context variables
170 	tc.putAll( context );
171 
172 	return tc;
173     }    
174 
175     private Properties extractVelocityProperties( Map context ) {
176 	Properties props = new Properties();
177 	String rLoader = null;
178 	if( (rLoader = (String)context.get( "resource.loader" )) == null )
179 	    rLoader = "file";
180 	props.put( "resource.loader", rLoader );
181 	rLoader = rLoader + ".";
182 	Iterator<Map.Entry> it = context.entrySet().iterator();
183 	while( it.hasNext() ) {
184 	    Map.Entry me = it.next();
185 	    if( (me.getKey() != null) &&
186 		(me.getKey().toString().startsWith( rLoader )) )
187 		props.put( me.getKey().toString(), me.getValue() );
188 	}
189 	return props;
190     }
191 
192     private VelocityEngine getVelocityEngine( Properties props ) 
193 	throws TemplateException {
194 
195 	if( vEngine == null ) {
196 	    Properties vProps = extractVelocityProperties( props );
197 
198 	    log.debug( "Creating velocity engine using properties: "+vProps );
199 
200 	    vEngine = new VelocityEngine();
201  	    vEngine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
202  				 "org.apache.velocity.runtime.log.Log4JLogChute" );
203  	    vEngine.setProperty("runtime.log.logsystem.log4j.logger",
204  				LOGGER_NAME);
205 	    log.debug( "Velocity engine logging setup done." );
206 	    try {
207 		vEngine.init( vProps );
208 		log.debug( "Velocity engine initialized." );
209 	    }
210 	    catch( Exception ex ) {
211 		log.error( ex );
212 		throw new TemplateException( ex.getMessage() );
213 	    }
214 	}
215 	return vEngine;
216     }
217 
218     private Properties createVelocityProperties( File templDir, Map context ) {
219 	Properties props = new Properties();
220 
221 	StringBuilder stb = new StringBuilder( System.getProperty( "user.dir" ) );
222 	if( templDir.isFile() ) {
223 	    File parent = templDir.getParentFile();
224 	    if( parent != null ) {
225 		stb.append( ", " );
226 		stb.append( parent );
227 	    }
228 	}
229 	else {
230 	    stb.append( ", " );
231 	    stb.append( templDir.getAbsoluteFile() );
232 	}
233 	    
234 	props.put("file.resource.loader.path", stb.toString() );
235 	props.put("file.resource.loader.modificationCheckInterval", "120" );
236 	props.put("file.resource.loader.cache", "true" );
237 
238 	if( context != null ) {
239 	    String rLoader = null;
240 	    if( (rLoader = (String)context.get( "resource.loader" )) == null )
241 		rLoader = "file";
242 	    props.put( "resource.loader", rLoader );
243 	    props.putAll( context );
244 	}
245 	else
246 	    props.put( "resource.loader", "file" );
247 
248 	return props;
249     }
250 
251     private Writer createWriter( File templDir, File outDir ) 
252 	throws IOException {
253 
254 	File outD = null;
255 	Writer outW = null;
256 	if( outDir == null ) {
257 	    if( templDir.isDirectory() )
258 		outD = templDir;
259 	    else
260 		outD = templDir.getParentFile();
261 	}
262 	else {
263 	    if( outDir.exists() && outDir.isDirectory() )
264 		outD = outDir;
265 	    else 
266 		outW = new FileWriter( outDir );
267 	}
268 
269 	if( outW == null )
270 	    outW = new FileWriter( File.createTempFile( "nextprot", ".rdf", outD ) );
271 	    
272 	return outW;
273     }
274 
275     /**
276      * Process the template(s).
277      *
278      * @param templDir a directory or file where the template is located.
279      * @param outDir a directory or file where the output should be written to. 
280      * This can be null (current directory is used instead)
281      * @param context a map of properties for use inside the velocity context.
282      * @exception TemplateException signals abnormal behavior.
283      */
284     public void processTemplates( File templDir, File outDir, Map context ) 
285 	throws TemplateException {
286 
287 	if( !templDir.exists() ) {
288 	    log.error( "Template path does not exist: "+templDir );
289 	    throw new TemplateException( "Template path does not exist: "+templDir );
290 	}
291 
292 	Properties props = createVelocityProperties( templDir, context );
293 	String tName = MAIN_TEMPLATE;
294 	if( templDir.isFile() ) 
295 	    tName = templDir.getName();
296 
297 	log.debug( "Using template "+tName );
298 
299 	VelocityEngine ve = getVelocityEngine( props );
300 	Context vc = createVelocityContext( templDir, context );
301 
302 	try {
303 	    Writer outW = createWriter( templDir, outDir );
304 	    if( !ve.mergeTemplate( tName, vc, outW ) ) 
305 		log.error( "Cannot transform template "+tName );
306 	}
307 	catch( Exception ex ) {
308 	    log.error( ex );
309 	    throw new TemplateException( ex.getMessage() );
310 	}
311     }
312 
313     /**
314      * Process the template(s).
315      *
316      * @param templDir a directory or file where the template is located.
317      * @exception TemplateException signals abnormal behavior.
318      */
319     public void processTemplates( File templDir )
320 	throws TemplateException {
321 
322 	processTemplates( templDir, null, null );
323     }
324 
325     private static Properties parse( String[] argv ) {
326 	Options options = new Options();
327 
328 	options.addOption( OptionBuilder.withLongOpt( OPT_OUTPUT )
329 			   .withDescription( "output directory, defaults to the current directory" )
330 			   .hasArgs()
331 			   .withArgName("DIRECTORY")
332 			   .create( "o" ) );
333 
334  	options.addOption( OptionBuilder.withLongOpt( OPT_VELOSURF )
335  			   .withDescription( "attribute key for accessing the velosurf functionality, defaults to \""+
336 					     VELOSURF_KEY+"\"" ) 
337  			   .hasArgs()
338  			   .withArgName("KEY")
339  			   .create( "k" ) );
340 
341  	options.addOption( OptionBuilder.withLongOpt( OPT_MODEL )
342  			   .withDescription( "database model mapping file, defaults to \""+DATABASE_CONFIG+"\"" ) 
343  			   .hasArgs()
344  			   .withArgName("MODELFILE")
345  			   .create( "m" ) );
346 
347 // 	options.addOption( OptionBuilder.withLongOpt( OPT_USER )
348 // 			   .withDescription( "database user's name, defaults to "+DEFAULT_USER )
349 // 			   .hasArgs()
350 // 			   .withArgName("NAME")
351 // 			   .create( "u" ) );
352 
353 // 	options.addOption( OptionBuilder.withLongOpt( OPT_PASSWORD )
354 // 			   .withDescription( "user's password" )
355 // 			   .hasArgs()
356 // 			   .withArgName("PASSWORD")
357 // 			   .create( "p" ) );
358 
359 
360 	options.addOption( "v", "verbose", false, "switch verbose mode on" );
361 	options.addOption( "h", "help", false, "print help screen" );
362 
363 	CommandLineParser parser = new PosixParser();
364 	CommandLine line = null;
365 	HelpFormatter help = new HelpFormatter();
366 
367 	Properties props = new Properties();
368  	props.put( OPT_VELOSURF, VELOSURF_KEY );
369  	props.put( OPT_MODEL, DATABASE_CONFIG );
370 
371 // 	props.put( OPT_USER, DEFAULT_USER ); 
372 // 	props.put( OPT_PASSWORD, DEFAULT_PASSWORD ); 
373 
374 	try {
375 	    line = parser.parse( options, argv );	    
376 	}
377 	catch( ParseException pex ) {
378 	    log.error( pex );
379 	    error( "parsing command line: "+
380 		   ((pex.getMessage()!=null)?pex.getMessage():"" ), 
381 		   1 );
382 	}
383 	if( line.hasOption( "h" ) ) {
384 	    help.printHelp( USAGE, options );
385 	    System.exit( 1 );
386 	}
387 
388 	Option[] opts = line.getOptions();
389 	for( int i = 0; i < opts.length; i++ ) {
390 	    if( opts[i].getValue() != null )
391 		props.put( opts[i].getLongOpt(), opts[i].getValue() );
392 	}
393 
394 	String[] al = line.getArgs();
395 	if( (al.length <= 0) && (opts.length <= 0) ) {
396 	    help.printHelp( USAGE, options );
397 	    error( "missing arguments", 2 );
398 	}
399 	
400 	for( int i = 0; i < al.length; i++ ) 
401 	    props.put( "_arg"+i, al[i] );
402 
403 	File testF = new File( props.getProperty( "_arg0", System.getProperty( "user.dir", "." ) ) );
404 	if( !testF.exists() )
405 	    error( testF+" does not exist", 3 );
406 	props.put( "templatePath", testF ); 
407 	
408 	return props;
409     }
410 
411     private static void error( String msg, int rc ) {
412 	System.out.println();
413 	System.out.println( "Error ["+Math.abs(rc)+"]: "+msg );
414 	System.out.println();
415 	if( rc > 0 )
416 	    System.exit( rc );
417     }
418 
419     private static void logo() {
420 	System.out.println();
421 	System.out.println( "nextor - a template based tool to transform relational database models into rdf");
422 	System.out.println( "mailto:Oliver.Karch@merckgroup.com");
423 	System.out.println( "(c) 2012 merckgroup.com");
424 	System.out.println();
425     }
426 
427     public static void main( String[] args ) {
428 	logo();
429 	Properties props = parse( args );
430 	log.debug( "Command line properties: "+props );
431 	TemplateAssembler ass = new TemplateAssembler();
432 	try {
433 	    File templDir = (File)props.get( "templatePath" );
434 	    File outDir = new File( props.getProperty( OPT_OUTPUT, System.getProperty( "user.dir", "" ) ) );
435 	    ass.processTemplates( templDir, outDir, props );
436 	}
437 	catch( TemplateException ex ) {
438 	    ex.printStackTrace();
439 	    error( ex.getMessage(), 4 );
440 	}
441     }
442 }