001    package hirondelle.web4jtools.util;
002    
003    import java.util.logging.*;
004    import javax.servlet.jsp.JspException;
005    import hirondelle.web4j.ui.tag.TagHelper;
006    import hirondelle.web4j.util.Util;
007    import hirondelle.web4jtools.codegenerator.feature.Feature;
008    import hirondelle.web4jtools.codegenerator.feature.FeatureAction;
009    
010    import java.io.*;
011    import hirondelle.web4j.util.Consts;
012    import javax.servlet.ServletConfig;
013    import hirondelle.web4jtools.util.Ensure;
014    
015    /** 
016    * Echo the trimmed body, while also saving it as a file on the server as a side-effect.
017    * 
018    * <P>Uses two settings in <tt>web.xml</tt> : 
019    * <ul>
020    * <li><tt>BaseDirectoryForGeneratedFiles</tt> - the base or root directory for your classes. 
021    * This code generator follows the 
022    * <a href='http://www.web4j.com/UserGuide.jsp#DirectoryStructure'>package-by-feature</a> style 
023    * recommended by WEB4J, in which all items related to a given feature are placed in a single directory: 
024    * JSP, Action, Model, DAO, and .sql file. A value of <tt>'Disabled'</tt> for this setting will turn off 
025    * all file generation. When enabled, the value has the form <tt>[project-root]/WEB-INF/classes/</tt>.
026    * <li><tt>CharacterSetForGeneratedFiles</tt> - controls the character encoding of generated files.
027    * </ul>
028    * 
029    * <P>Example use case in a JSP :
030    * <PRE>
031    &lt;w:saveToFile named='statements.sql'&gt;
032     ...JSP content...
033    &lt;/w:saveToFile&gt;
034    * </PRE>
035    * The exact location of the generated files depends on the package name. If the above snippet is 
036    * for a feature having the package <tt>com.xyz.jet</tt>, then the generated file will be 
037    * <P><tt>[project-root]/WEB-INF/classes/com/xyz/jet/statements.sql</tt>
038    */
039    public final class SaveToFile extends TagHelper {
040    
041      /** Read in configuration from <tt>web.xml</tt>  */
042      static public void readConfig(ServletConfig aConfig){
043        fBASE_DIR = aConfig.getInitParameter(BASE_DIR);
044        fCHARSET = aConfig.getInitParameter(CHARSET);
045        Ensure.isPresentInWebXml(BASE_DIR, fBASE_DIR);
046        Ensure.isPresentInWebXml(CHARSET, fCHARSET);
047      }
048      
049      /** Set the name of the emitted file.  */
050      public void setNamed(String aFileName){
051        fFileName = aFileName;
052      }
053      
054      @Override protected String getEmittedText(String aBody) throws JspException, IOException {
055        if( isEnabled() ) {
056          saveToFile(aBody.trim(), getOutputFileName());
057        }
058        else {
059          fLogger.fine("Not saving to file, since disabled.");
060        }
061        return aBody;
062      }
063      
064      /** 
065      * Return <tt>true</tt> only if <tt>BaseDirectoryForGeneratedFiles</tt> (<tt>web.xml</tt>) is not 
066      * set to <tt>'Disabled'</tt> (ignoring case). 
067      */
068      public static boolean isEnabled(){
069        return ! DISABLED.equalsIgnoreCase(fBASE_DIR);  
070      }
071      
072      // PRIVATE //
073      private String fFileName;
074      
075      private static final String BASE_DIR = "BaseDirectoryForGeneratedFiles";
076      private static final String DISABLED = "Disabled";
077      private static String fBASE_DIR;
078      
079      private static final String CHARSET = "CharacterSetForGeneratedFiles"; 
080      private static String fCHARSET;
081      
082      private static final Logger fLogger = Util.getLogger(SaveToFile.class);
083      
084      private String getOutputFileName() throws IOException {
085        String directory = getProjectRootForClasses() + getPackagePath();
086        ensureDirectoryExists(directory);
087        ensureFileExists();
088        String result = directory + fFileName;
089        fLogger.fine("Saving to file named : " + result);
090        return result;
091      }
092      
093      private String getProjectRootForClasses(){
094        return fBASE_DIR;
095      }
096      
097      private String getPackagePath(){
098        Feature feature = (Feature)getRequest().getSession().getAttribute(FeatureAction.FEATURE_KEY);
099        return addDirectorySeparators(feature.getPackageName());
100      }
101    
102      private String addDirectorySeparators(String aPackageName){
103        return Util.replace(aPackageName, ".", Consts.FILE_SEPARATOR) + Consts.FILE_SEPARATOR;
104      }
105      
106      /** Emit the text into the file.  */
107      private void saveToFile(String aBody, String aOutputFileName) throws IOException {
108        Writer out = null;
109        try {
110            FileOutputStream fos = new FileOutputStream(aOutputFileName);
111            out = new BufferedWriter(new OutputStreamWriter(fos, fCHARSET));
112            out.write(aBody);
113        }
114        finally {
115          if ( out != null) out.close();
116        }
117      }
118      
119      private void ensureDirectoryExists(String aDirName){
120        File dir = new File(aDirName);
121        dir.mkdirs();
122      }
123      
124      private void ensureFileExists() throws IOException {
125        File file = new File(fFileName);
126        file.createNewFile(); //does not create if already exists
127      }
128    }