001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.velocity; 018 019import java.io.StringWriter; 020import java.util.Map; 021 022import org.apache.velocity.VelocityContext; 023import org.apache.velocity.app.Velocity; 024import org.apache.wicket.Component; 025import org.apache.wicket.WicketRuntimeException; 026import org.apache.wicket.behavior.Behavior; 027import org.apache.wicket.markup.head.IHeaderResponse; 028import org.apache.wicket.markup.head.StringHeaderItem; 029import org.apache.wicket.model.IModel; 030import org.apache.wicket.util.lang.Args; 031import org.apache.wicket.util.string.Strings; 032 033/** 034 * An IHeaderContributor implementation that renders a velocity template and writes it to the 035 * response. The template is loaded via Velocity's resource loading mechanism, as defined in your 036 * velocity.properties. If you do not have a velocity.properties for your app, it will default to a 037 * directory "templates" in the root of your app. 038 */ 039public class VelocityContributor extends Behavior 040{ 041 private static final long serialVersionUID = 1L; 042 043 private String encoding = "ISO-8859-1"; 044 045 private final IModel<? extends Map<String, Object>> model; 046 047 private final String templateName; 048 049 /** 050 * The templateName needs to have the full path relative to where the resource loader starts 051 * looking. For example, if there is a template next to this class in the package called foo.vm, 052 * and you have configured the ClassPathResourceLoader, template name will then be 053 * "wicket/contrib/util/resource/foo.vm". Wicket provides a nice utility 054 * {@link org.apache.wicket.util.lang.Packages} for this. 055 * 056 * @param templateName 057 * @param model 058 */ 059 public VelocityContributor(final String templateName, final IModel<? extends Map<String, Object>> model) 060 { 061 Args.notNull(model, "model"); 062 063 this.templateName = templateName; 064 this.model = model; 065 } 066 067 /** 068 * {@inheritDoc} 069 */ 070 @Override 071 public void detach(final Component c) 072 { 073 model.detach(); 074 } 075 076 /** 077 * @return The encoding 078 */ 079 public String getEncoding() 080 { 081 return encoding; 082 } 083 084 /** 085 * {@inheritDoc} 086 */ 087 @Override 088 public void renderHead(final Component component, final IHeaderResponse response) 089 { 090 CharSequence s = evaluate(); 091 if (null != s) 092 { 093 response.render(StringHeaderItem.forString(s)); 094 } 095 } 096 097 /** 098 * @param encoding 099 * The encoding 100 */ 101 public void setEncoding(final String encoding) 102 { 103 this.encoding = encoding; 104 } 105 106 /** 107 * @return whether to escape HTML characters. The default value is false 108 */ 109 protected boolean escapeHtml() 110 { 111 return false; 112 } 113 114 /** 115 * Evaluate the template. 116 * 117 * @return The evaluated template 118 */ 119 protected final CharSequence evaluate() 120 { 121 if (!Velocity.resourceExists(templateName)) 122 { 123 return null; 124 } 125 126 // create a Velocity context object using the model if set 127 final VelocityContext ctx = new VelocityContext(model.getObject()); 128 129 // create a writer for capturing the Velocity output 130 StringWriter writer = new StringWriter(); 131 132 try 133 { 134 // execute the velocity script and capture the output in writer 135 if (!Velocity.mergeTemplate(templateName, encoding, ctx, writer)) 136 { 137 return null; 138 } 139 140 // replace the tag's body the Velocity output 141 CharSequence result = writer.getBuffer(); 142 143 if (escapeHtml()) 144 { 145 // encode the result in order to get valid html output that 146 // does not break the rest of the page 147 result = Strings.escapeMarkup(result.toString()); 148 } 149 return result; 150 } 151 catch (Exception e) 152 { 153 throw new WicketRuntimeException("Error while executing velocity script: " + 154 templateName, e); 155 } 156 } 157}