001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2014 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.generator.gsp; 023 024import groovy.lang.Binding; 025import groovy.lang.GroovyShell; 026import groovy.lang.Script; 027 028import java.io.BufferedReader; 029import java.io.File; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.InputStreamReader; 033import java.io.Reader; 034import java.io.Writer; 035import java.net.URI; 036import java.nio.charset.Charset; 037import java.util.List; 038import java.util.Map; 039 040import org.codehaus.groovy.control.CompilationFailedException; 041import org.codehaus.groovy.runtime.InvokerHelper; 042import org.granite.generator.Template; 043import org.granite.generator.exception.TemplateCompilationException; 044import org.granite.generator.exception.TemplateExecutionException; 045import org.granite.generator.exception.TemplateParsingException; 046import org.granite.generator.gsp.token.Token; 047import org.granite.util.URIUtil; 048 049/** 050 * @author Franck WOLFF 051 */ 052public class GroovyTemplate implements Template { 053 054 private final URI uri; 055 private final boolean base; 056 private final Charset charset; 057 058 private String source = null; 059 private Script script = null; 060 private long lastModified = -1L; 061 062 GroovyTemplate(URI uri, boolean base, Charset charset) { 063 if (uri == null || charset == null) 064 throw new IllegalArgumentException("uri and charset cannot be null"); 065 066 this.uri = uri; 067 this.base = base; 068 this.charset = charset; 069 } 070 071 protected void reset(boolean cleanSource) { 072 if (cleanSource) 073 source = null; 074 script = null; 075 lastModified = -1L; 076 } 077 protected void reset() { 078 reset(true); 079 } 080 081 @Override 082 public URI getUri() { 083 return uri; 084 } 085 086 public long getLastModified() { 087 return lastModified; 088 } 089 090 protected long readLastModified() { 091 long readLastModified = -1L; 092 if ("file".equals(uri.getScheme())) { 093 readLastModified = new File(uri).lastModified(); 094 if (readLastModified == 0L) 095 readLastModified = -1L; 096 } 097 else 098 readLastModified = Long.MAX_VALUE; 099 return readLastModified; 100 } 101 102 public boolean isOutdated() { 103 long newLastModified = readLastModified(); 104 return script != null && (newLastModified == -1 || lastModified < newLastModified); 105 } 106 107 @Override 108 public boolean isBase() { 109 return base; 110 } 111 112 @Override 113 public String getMarkup() { 114 try { 115 return URIUtil.getContentAsString(uri); 116 } catch (Exception e) { 117 return "Could not load uri content: " + uri + " (" + e.toString() + ")"; 118 } 119 } 120 121 @Override 122 public String getSource() { 123 return source; 124 } 125 126 @Override 127 public void compile() throws IOException, TemplateParsingException, TemplateCompilationException { 128 reset(); 129 130 InputStream is = null; 131 try { 132 is = URIUtil.getInputStream(uri, getClass().getClassLoader()); 133 134 lastModified = readLastModified(); 135 136 Reader reader = new BufferedReader(new InputStreamReader(is, charset)); 137 138 Parser parser = new Parser(); 139 List<Token> tokens = parser.parse(reader); 140 141 GroovyRenderer renderer = new GroovyRenderer(); 142 source = renderer.renderSource(tokens); 143 144 // Warning: the classloader for javax annotations is defined in BuilderParentClassLoader 145 // in the case of the Eclipse generator 146 GroovyShell shell = new GroovyShell(Thread.currentThread().getContextClassLoader()); 147 script = shell.parse(source); 148 } catch (ParseException e) { 149 throw new TemplateParsingException(this, "Could not parse template: " + uri, e); 150 } catch (CompilationFailedException e) { 151 throw new TemplateCompilationException(this, "Could not compile template: " + uri, e); 152 } finally { 153 if (script == null) 154 reset(false); 155 156 if (is != null) 157 is.close(); 158 } 159 } 160 161 @Override 162 public void execute(Map<String, Object> bindings, Writer out) 163 throws IOException, TemplateParsingException, TemplateCompilationException, TemplateExecutionException { 164 165 if (script == null) 166 compile(); 167 168 try { 169 Binding binding = (bindings == null ? new Binding() : new Binding(bindings)); 170 Script scriptInstance = InvokerHelper.createScript(script.getClass(), binding); 171 scriptInstance.setProperty("out", out); 172 scriptInstance.run(); 173 out.flush(); 174 } catch (IOException e) { 175 throw e; 176 } catch (Exception e) { 177 throw new TemplateExecutionException(this, "Could not execute template: " + uri, e); 178 } 179 } 180 181 @Override 182 public boolean equals(Object obj) { 183 if (obj == this) 184 return true; 185 if (!(obj instanceof GroovyTemplate)) 186 return false; 187 return uri.equals(((GroovyTemplate)obj).uri); 188 } 189 190 @Override 191 public int hashCode() { 192 return uri.hashCode(); 193 } 194}