001    /*
002     * ----------------------------------------------------------------------
003     * Copyright (C) 2009 Enrique Lara (k957@68k.org)
004     *
005     * TinLizard is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU Lesser General Public License
007     * as published by the Free Software Foundation; either version 3.0
008     * of the License, or (at your option) any later version.
009     *
010     * TinLizard is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013     * GNU Lesser General Public License for more details.
014     *
015     * You should have received a copy of the GNU Lesser General Public License
016     * along with TinLizard. If not, see http://www.gnu.org/licenses/.
017     * ----------------------------------------------------------------------
018     */
019    package tinlizard.dao.io;
020    
021    import freemarker.template.Configuration;
022    import freemarker.template.DefaultObjectWrapper;
023    import freemarker.template.Template;
024    
025    import tinlizard.dao.MashDao;
026    
027    import tinlizard.util.Messages;
028    
029    import java.io.File;
030    import java.io.FileNotFoundException;
031    import java.io.FileOutputStream;
032    import java.io.IOException;
033    import java.io.InputStream;
034    import java.io.Writer;
035    import java.util.HashMap;
036    import java.util.Locale;
037    import java.util.Map;
038    
039    import org.apache.commons.lang.StringUtils;
040    import org.apache.log4j.Logger;
041    
042    /**
043     * Freemarker implementation of MashDao.
044     */
045    public final class MashDaoImpl implements MashDao {
046        private static final Logger LOG = Logger.getLogger(MashDaoImpl.class);
047        private static final int BUFFER_SIZE = 128;
048        private String mashFilesDirectory;
049        private File mashDir = new File("/tmp");
050        private File resourcesDir = new File("/tmp");
051        private Configuration cfg = null;
052        private static final int SECONDS_PER_MINUTE = 60;
053        private int delayMinutes = 5;
054    
055        public String getMashFilesDirectory() {
056            return mashFilesDirectory;
057        }
058    
059        public void setMashFilesDirectory(final String mashFilesDirectory) {
060            this.mashFilesDirectory = mashFilesDirectory;
061    
062            mashDir = new File(mashFilesDirectory);
063            resourcesDir = new File(mashDir, "resources");
064    
065            if (!mashDir.isDirectory()) {
066                mashDir.mkdirs();
067            }
068    
069            if (!resourcesDir.isDirectory()) {
070                resourcesDir.mkdir();
071            }
072    
073            try {
074                cfg = new Configuration();
075                cfg.setDirectoryForTemplateLoading(mashDir);
076                cfg.setObjectWrapper(new DefaultObjectWrapper());
077                cfg.setTemplateUpdateDelay(delayMinutes * SECONDS_PER_MINUTE);
078            } catch (Exception e) {
079                throw new RuntimeException(Messages.error_0300(), e);
080            }
081        }
082    
083        public File getResourcesFile(final String path) {
084            if (path.indexOf("..") != -1) {
085                //XXX consider logging warning.
086                return null;
087            }
088    
089            File resFile = new File(resourcesDir, path);
090    
091            if (!resFile.getParentFile().exists()) {
092                return null;
093            }
094    
095            if (!resFile.exists()) {
096                String resourceClassPath = "/mash-files/resources" + path;
097    
098                if (copyFromClasspath(resFile, resourceClassPath)) {
099                    return resFile;
100                }
101            }
102    
103            return null;
104        }
105    
106        private boolean copyFromClasspath(final File resFile, final String resourceClassPath) {
107            InputStream is = getClass().getResourceAsStream(resourceClassPath);
108    
109            if (is == null) {
110                return false;
111            }
112    
113            if (!resFile.getParentFile().isDirectory()) {
114                resFile.getParentFile().mkdirs();
115            }
116    
117            FileOutputStream os = null;
118    
119            try {
120                os = new FileOutputStream(resFile);
121    
122                byte[] buffer = new byte[BUFFER_SIZE];
123                int nRead = -1;
124    
125                while ((nRead = is.read(buffer)) > -1) {
126                    os.write(buffer, 0, nRead);
127                }
128    
129                return true;
130            } catch (FileNotFoundException e) {
131                return false;
132            } catch (IOException e) {
133                return false;
134            } finally {
135                try {
136                    is.close();
137                } catch (IOException e) {
138                    LOG.trace("Ignore, nothing to do.");
139                }
140    
141                if (os != null) {
142                    try {
143                        os.close();
144                    } catch (IOException e) {
145                        LOG.trace("Ignore, nothing to do.");
146                    }
147                }
148            }
149        }
150    
151        public void applyTemplate(final Object it, final String template, final Writer out, final Locale locale, final String username) {
152            try {
153                boolean foundTemplate = false;
154    
155                String resourcePath = null;
156    
157                Class<?> clazz = it.getClass();
158    
159                while (!foundTemplate && !Object.class.equals(clazz)) {
160                    if (LOG.isDebugEnabled()) {
161                        LOG.debug("Class:" + clazz);
162                    }
163    
164                    File classMashDir = getClassMashDir(clazz);
165                    resourcePath = getResourcePath(clazz);
166    
167                    File templateFile = new File(classMashDir, template);
168                    foundTemplate = templateFile.exists();
169    
170                    if (!foundTemplate) {
171                        String resourceClassPath = "/mash-files/" + resourcePath + "/" + template;
172    
173                        if (LOG.isDebugEnabled()) {
174                            LOG.debug("Check:" + resourceClassPath);
175                        }
176    
177                        foundTemplate = copyFromClasspath(templateFile, resourceClassPath);
178                    }
179    
180                    clazz = clazz.getSuperclass();
181                }
182    
183                if (foundTemplate) {
184                    if (LOG.isDebugEnabled()) {
185                        LOG.debug("Found:" + resourcePath + "/" + template);
186                    }
187    
188                    Map<Object, Object> root = new HashMap<Object, Object>();
189                    root.put("it", it);
190    
191                    if (StringUtils.isNotBlank(username)) {
192                        root.put("username", username);
193                    }
194    
195                    Template temp = cfg.getTemplate(resourcePath + "/" + template, locale);
196                    temp.process(root, out);
197                }
198            } catch (Exception e) {
199                LOG.info("Problems Applying template:" + template, e);
200            }
201        }
202    
203        private String getResourcePath(final Class<?extends Object> clazz) {
204            String clazzName = clazz.getName();
205            String clazzDir = clazzName.replaceAll("\\.", "/");
206    
207            return clazzDir;
208        }
209    
210        private File getClassMashDir(final Class<?extends Object> clazz) {
211            String clazzName = clazz.getName();
212            String clazzDir = clazzName.replaceAll("\\.", "/");
213    
214            return new File(mashDir, clazzDir);
215        }
216    }