View Javadoc

1   /*
2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector;
18  
19  import java.io.StringWriter;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.velocity.Template;
25  import org.apache.velocity.VelocityContext;
26  import org.apache.velocity.app.VelocityEngine;
27  import org.apache.velocity.runtime.resource.util.StringResourceRepository;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
32  import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
33  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ResolutionPlugIn;
34  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
35  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.attributeDefinition.AttributeDefinition;
36  import edu.internet2.middleware.shibboleth.common.util.StringResourceLoader;
37  
38  /**
39   * A velocity based template engine that pulls information from a resolution context for use within the template.
40   */
41  public class TemplateEngine {
42  
43      /** Class logger. */
44      private final Logger log = LoggerFactory.getLogger(TemplateEngine.class);
45  
46      /** Velocity engine to use to render templates. */
47      private VelocityEngine velocity;
48  
49      /**
50       * Constructor.
51       * 
52       * @param engine velocity engine used to evaluate templates
53       */
54      public TemplateEngine(VelocityEngine engine) {
55          velocity = engine;
56      }
57  
58      /**
59       * Registers a template under a given name.
60       * 
61       * @param templateName name to register the template under
62       * @param template template to register
63       */
64      public void registerTemplate(String templateName, String template) {
65          StringResourceRepository repository = StringResourceLoader.getRepository();
66          repository.putStringResource(templateName, template);
67      }
68  
69      /**
70       * Create a statement from a give template by replacing it's macro's with information within the resolution context.
71       * 
72       * @param templateName name of the template
73       * @param resolutionContext the current resolution context
74       * @param dependencies the list of resolution plug-in dependencies that will provider attributes
75       * @param escapingStrategy strategy used to escape values, may be null if no escaping is necessary
76       * 
77       * @return constructed statement
78       * 
79       * @throws AttributeResolutionException thrown if the given template can not be populated because it is malformed or
80       *             the given data connectors or attribute definitions error out during resolution
81       */
82      public String createStatement(String templateName, ShibbolethResolutionContext resolutionContext,
83              List<String> dependencies, CharacterEscapingStrategy escapingStrategy) throws AttributeResolutionException {
84          VelocityContext vContext = createVelocityContext(resolutionContext, dependencies, escapingStrategy);
85  
86          try {
87              log.trace("Populating the following {} template", templateName);
88  
89              StringWriter output = new StringWriter();
90              Template template = velocity.getTemplate(templateName);
91              template.merge(vContext, output);
92              return output.toString();
93          } catch (Exception e) {
94              log.error("Unable to populate " + templateName + " template", e);
95              throw new AttributeResolutionException("Unable to evaluate template", e);
96          }
97      }
98  
99      /**
100      * Creates the velocity context from the given resolution context.
101      * 
102      * @param resolutionContext the resolution context containing the currently resolved attribute information
103      * @param dependencies resolution plug-in dependencies that will provide attributes to the velocity context
104      * @param escapingStrategy strategy used to escape values
105      * 
106      * @return the velocity context to use when evaluating the template
107      * 
108      * @throws AttributeResolutionException thrown if a resolution plugin errors out while resolving its attributes
109      */
110     @SuppressWarnings("unchecked")
111     protected VelocityContext createVelocityContext(ShibbolethResolutionContext resolutionContext,
112             List<String> dependencies, CharacterEscapingStrategy escapingStrategy) throws AttributeResolutionException {
113         log.trace("Populating velocity context");
114         VelocityContext vCtx = new VelocityContext();
115         vCtx.put("requestContext", resolutionContext.getAttributeRequestContext());
116 
117         ResolutionPlugIn plugin;
118         Map<String, BaseAttribute> attributes;
119         BaseAttribute attribute;
120         for (String dependencyId : dependencies) {
121             plugin = resolutionContext.getResolvedPlugins().get(dependencyId);
122             if (plugin instanceof DataConnector) {
123                 log.trace("Resolving attributes from data connector {}", dependencyId);
124                 attributes = ((DataConnector) plugin).resolve(resolutionContext);
125 
126                 for (String attributeId : attributes.keySet()) {
127                     vCtx.put(attributeId, prepareAttributeValues(attributes.get(attributeId), escapingStrategy));
128                 }
129             } else if (plugin instanceof AttributeDefinition) {
130                 log.trace("Resolving attributes from attribute definition {}", dependencyId);
131                 attribute = ((AttributeDefinition) plugin).resolve(resolutionContext);
132                 if (!vCtx.containsKey(attribute.getId())) {
133                     vCtx.put(attribute.getId(), new ArrayList<String>());
134                 }
135                 ((List<String>) vCtx.get(attribute.getId())).addAll(attribute.getValues());
136             } else {
137                 log.trace("Unable to locate resolution plugin {}", dependencyId);
138             }
139         }
140 
141         return vCtx;
142     }
143 
144     /**
145      * Prepares an attributes values for use within a template.
146      * 
147      * @param attribute attribute whose values are to be prepared
148      * @param escapingStrategy character escaping strategy to be sued
149      * 
150      * @return prepared values
151      */
152     protected List<Object> prepareAttributeValues(BaseAttribute attribute, CharacterEscapingStrategy escapingStrategy) {
153         ArrayList<Object> preparedValues = new ArrayList<Object>();
154 
155         if (attribute == null || attribute.getValues() == null || attribute.getValues().isEmpty()) {
156             return preparedValues;
157         }
158 
159         if (escapingStrategy == null) {
160             preparedValues.addAll(attribute.getValues());
161         } else {
162             for (Object value : attribute.getValues()) {
163                 if(value instanceof String){
164                     preparedValues.add(escapingStrategy.escape(value.toString()));
165                 }else{
166                     preparedValues.add(value);
167                 }
168             }
169         }
170 
171         return preparedValues;
172     }
173 
174     /**
175      * Represents a domain specific strategy for escaping values used within a template.
176      */
177     public interface CharacterEscapingStrategy {
178 
179         /**
180          * Creates a new string with necessary escaping rules.
181          * 
182          * @param value the value to be escaped
183          * 
184          * @return the escaped string
185          */
186         public String escape(String value);
187     }
188 }