1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.danube.velocity;
14
15 import org.abstracthorizon.danube.connection.Connection;
16 import org.abstracthorizon.danube.connection.ConnectionException;
17 import org.abstracthorizon.danube.http.HTTPConnection;
18 import org.abstracthorizon.danube.mvc.ModelAndView;
19 import org.abstracthorizon.danube.mvc.View;
20 import org.abstracthorizon.danube.support.InitializationException;
21 import org.abstracthorizon.danube.support.URLUtils;
22
23 import java.io.File;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.Writer;
27 import java.net.URL;
28 import java.util.Map;
29
30 import org.apache.velocity.Template;
31 import org.apache.velocity.VelocityContext;
32 import org.apache.velocity.app.Velocity;
33 import org.apache.velocity.context.Context;
34 import org.apache.velocity.io.VelocityWriter;
35 import org.apache.velocity.runtime.RuntimeConstants;
36 import org.apache.velocity.runtime.RuntimeSingleton;
37 import org.apache.velocity.servlet.VelocityServlet;
38 import org.apache.velocity.util.SimplePool;
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class VelocityViewAdapter implements View {
53
54
55 public static final String CONTENT_TYPE = "default.contentType";
56
57
58 public static final String DEFAULT_CONTENT_TYPE = "text/html";
59
60
61 public static final String DEFAULT_OUTPUT_ENCODING = "ISO-8859-1";
62
63
64 private static SimplePool writerPool = new SimplePool(40);
65
66
67 protected URL templatesURL;
68
69
70 protected File templatesPath;
71
72
73 protected String suffix;
74
75
76
77
78
79 protected String contentType;
80
81
82
83
84
85 public VelocityViewAdapter() throws Exception {
86 }
87
88
89
90
91
92 public void init() throws InitializationException {
93 contentType = RuntimeSingleton.getString(CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
94 String encoding = RuntimeSingleton.getString(RuntimeConstants.OUTPUT_ENCODING, DEFAULT_OUTPUT_ENCODING);
95
96
97 if (!DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding)) {
98 int index = contentType.lastIndexOf("charset");
99 if (index < 0) {
100
101
102 contentType += "; charset=" + encoding;
103 } else {
104
105 Velocity.warn("VelocityViewServlet: Charset was already " + "specified in the Content-Type property. "
106 + "Output encoding property will be ignored.");
107 }
108 }
109 Velocity.info("VelocityViewServlet: Default content-type is: " + contentType);
110
111 Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
112 Velocity.setProperty("file.resource.loader.class", AbsoluteFileResourceLoader.class.getName());
113
114 Velocity.setProperty("file.resource.loader.path", "");
115
116 try {
117 Velocity.init();
118 } catch (Exception e) {
119 throw new InitializationException(e);
120 }
121 }
122
123
124
125
126
127
128
129 public void render(Connection connection, ModelAndView modelAndView) throws ConnectionException {
130 try {
131 Context context = createContext(modelAndView.getModel());
132
133 Template template = findTemplate(context, modelAndView.getView());
134
135 if (template == null) {
136 Velocity.warn("VelocityViewServlet: couldn't find template to match request.");
137 return;
138 }
139
140 HTTPConnection httpConnection = (HTTPConnection)connection.adapt(HTTPConnection.class);
141 mergeTemplate(template, context, httpConnection);
142 } catch (IOException ioException) {
143 throw new ConnectionException(ioException);
144 } catch (Exception exception) {
145 throw new ConnectionException(exception);
146 }
147 }
148
149
150
151
152
153
154
155
156
157 protected Template findTemplate(Context ctx, String view) throws Exception {
158 if (templatesPath != null) {
159 File file = new File(templatesPath, view + getSuffix());
160 return RuntimeSingleton.getTemplate(file.getAbsolutePath());
161 }
162
163 if (templatesURL != null) {
164 URL newURL = URLUtils.addPath(templatesURL, view + getSuffix());
165 return RuntimeSingleton.getTemplate(newURL.toString());
166 }
167
168 throw new FileNotFoundException("One of templatesPath or templatesURL must be set.");
169 }
170
171
172
173
174
175
176
177 @SuppressWarnings("unchecked")
178 protected Context createContext(Map attributes) {
179 VelocityContext ctx = new VelocityContext(attributes);
180 return ctx;
181 }
182
183
184
185
186
187
188
189
190 protected void mergeTemplate(Template template, Context context, HTTPConnection connection) throws Exception {
191 connection.getResponseHeaders().putOnly("Content-Type", contentType);
192
193 VelocityWriter vw = null;
194 Writer writer = (Writer)connection.adapt(Writer.class);
195 try {
196 vw = (VelocityWriter) writerPool.get();
197 if (vw == null) {
198 vw = new VelocityWriter(writer, 4 * 1024, true);
199 } else {
200 vw.recycle(writer);
201 }
202 template.merge(context, vw);
203 } finally {
204 if (vw != null) {
205 try {
206
207
208
209 vw.flush();
210
211
212
213 vw.recycle(null);
214 writerPool.put(vw);
215 } catch (Exception e) {
216 Velocity.debug("VelocityViewServlet: " + "Trouble releasing VelocityWriter: " + e.getMessage());
217 }
218 }
219 }
220 }
221
222
223
224
225
226 public File getTemplatesPath() {
227 return templatesPath;
228 }
229
230
231
232
233
234 public void setTemplatesPath(File templatesPath) {
235 this.templatesPath = templatesPath;
236 }
237
238
239
240
241
242 public URL getTemplatesURL() {
243 return templatesURL;
244 }
245
246
247
248
249
250 public void setTemplatesURL(URL templatesURL) {
251 this.templatesURL = templatesURL;
252 if (templatesURL.getProtocol().equals("file")) {
253 templatesPath = new File(templatesURL.getFile());
254 }
255 }
256
257
258
259
260
261 public String getContentType() {
262 return contentType;
263 }
264
265
266
267
268
269 public void setContentType(String contentType) {
270 this.contentType = contentType;
271 }
272
273
274
275
276
277 public String getSuffix() {
278 if (suffix == null) {
279 suffix = ".vm";
280 }
281 return suffix;
282 }
283
284
285
286
287
288 public void setSuffix(String suffix) {
289 this.suffix = suffix;
290 }
291 }