View Javadoc

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