1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * 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 package org.apache.jasper.compiler;
18
19 import java.io.IOException;
20 import java.net.URL;
21 import java.security.AccessController;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import javax.servlet.ServletContext;
27
28 import org.apache.jasper.Constants;
29 import org.apache.jasper.JasperException;
30 import org.apache.jasper.compiler.tagplugin.TagPlugin;
31 import org.apache.jasper.compiler.tagplugin.TagPluginContext;
32 import org.apache.tomcat.util.descriptor.tagplugin.TagPluginParser;
33 import org.apache.tomcat.util.security.PrivilegedGetTccl;
34 import org.apache.tomcat.util.security.PrivilegedSetTccl;
35 import org.xml.sax.SAXException;
36
37 /**
38  * Manages tag plugin optimizations.
39  *
40  * @author Kin-man Chung
41  */

42 public class TagPluginManager {
43
44     private static final String META_INF_JASPER_TAG_PLUGINS_XML =
45             "META-INF/org.apache.jasper/tagPlugins.xml";
46     private static final String TAG_PLUGINS_XML = "/WEB-INF/tagPlugins.xml";
47     private final ServletContext ctxt;
48     private HashMap<String, TagPlugin> tagPlugins;
49     private boolean initialized = false;
50
51     public TagPluginManager(ServletContext ctxt) {
52         this.ctxt = ctxt;
53     }
54
55     public void apply(Node.Nodes page, ErrorDispatcher err, PageInfo pageInfo)
56             throws JasperException {
57
58         init(err);
59         if (!tagPlugins.isEmpty()) {
60             page.visit(new NodeVisitor(this, pageInfo));
61         }
62     }
63
64     private void init(ErrorDispatcher err) throws JasperException {
65         if (initialized)
66             return;
67
68         String blockExternalString = ctxt.getInitParameter(
69                 Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
70         boolean blockExternal;
71         if (blockExternalString == null) {
72             blockExternal = true;
73         } else {
74             blockExternal = Boolean.parseBoolean(blockExternalString);
75         }
76
77         TagPluginParser parser;
78         ClassLoader original;
79         if (Constants.IS_SECURITY_ENABLED) {
80             PrivilegedGetTccl pa = new PrivilegedGetTccl();
81             original = AccessController.doPrivileged(pa);
82         } else {
83             original = Thread.currentThread().getContextClassLoader();
84         }
85         try {
86             if (Constants.IS_SECURITY_ENABLED) {
87                 PrivilegedSetTccl pa =
88                         new PrivilegedSetTccl(TagPluginManager.class.getClassLoader());
89                 AccessController.doPrivileged(pa);
90             } else {
91                 Thread.currentThread().setContextClassLoader(
92                         TagPluginManager.class.getClassLoader());
93             }
94
95             parser = new TagPluginParser(ctxt, blockExternal);
96
97             Enumeration<URL> urls =
98                     ctxt.getClassLoader().getResources(META_INF_JASPER_TAG_PLUGINS_XML);
99             while (urls.hasMoreElements()) {
100                 URL url = urls.nextElement();
101                 parser.parse(url);
102             }
103
104             URL url = ctxt.getResource(TAG_PLUGINS_XML);
105             if (url != null) {
106                 parser.parse(url);
107             }
108         } catch (IOException | SAXException e) {
109             throw new JasperException(e);
110         } finally {
111             if (Constants.IS_SECURITY_ENABLED) {
112                 PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
113                 AccessController.doPrivileged(pa);
114             } else {
115                 Thread.currentThread().setContextClassLoader(original);
116             }
117         }
118
119         Map<String, String> plugins = parser.getPlugins();
120         tagPlugins = new HashMap<>(plugins.size());
121         for (Map.Entry<String, String> entry : plugins.entrySet()) {
122             try {
123                 String tagClass = entry.getKey();
124                 String pluginName = entry.getValue();
125                 Class<?> pluginClass = ctxt.getClassLoader().loadClass(pluginName);
126                 TagPlugin plugin = (TagPlugin) pluginClass.getConstructor().newInstance();
127                 tagPlugins.put(tagClass, plugin);
128             } catch (Exception e) {
129                 err.jspError(e);
130             }
131         }
132         initialized = true;
133     }
134
135     /**
136      * Invoke tag plugin for the given custom tag, if a plugin exists for
137      * the custom tag's tag handler.
138      * <p/>
139      * The given custom tag node will be manipulated by the plugin.
140      */

141     private void invokePlugin(Node.CustomTag n, PageInfo pageInfo) {
142         TagPlugin tagPlugin = tagPlugins.get(n.getTagHandlerClass().getName());
143         if (tagPlugin == null) {
144             return;
145         }
146
147         TagPluginContext tagPluginContext = new TagPluginContextImpl(n, pageInfo);
148         n.setTagPluginContext(tagPluginContext);
149         tagPlugin.doTag(tagPluginContext);
150     }
151
152     private static class NodeVisitor extends Node.Visitor {
153         private final TagPluginManager manager;
154         private final PageInfo pageInfo;
155
156         public NodeVisitor(TagPluginManager manager, PageInfo pageInfo) {
157             this.manager = manager;
158             this.pageInfo = pageInfo;
159         }
160
161         @Override
162         public void visit(Node.CustomTag n) throws JasperException {
163             manager.invokePlugin(n, pageInfo);
164             visitBody(n);
165         }
166     }
167
168     private static class TagPluginContextImpl implements TagPluginContext {
169         private final Node.CustomTag node;
170         private final PageInfo pageInfo;
171         private final HashMap<String, Object> pluginAttributes;
172         private Node.Nodes curNodes;
173
174         TagPluginContextImpl(Node.CustomTag n, PageInfo pageInfo) {
175             this.node = n;
176             this.pageInfo = pageInfo;
177             curNodes = new Node.Nodes();
178             n.setAtETag(curNodes);
179             curNodes = new Node.Nodes();
180             n.setAtSTag(curNodes);
181             n.setUseTagPlugin(true);
182             pluginAttributes = new HashMap<>();
183         }
184
185         @Override
186         public TagPluginContext getParentContext() {
187             Node parent = node.getParent();
188             if (!(parent instanceof Node.CustomTag)) {
189                 return null;
190             }
191             return ((Node.CustomTag) parent).getTagPluginContext();
192         }
193
194         @Override
195         public void setPluginAttribute(String key, Object value) {
196             pluginAttributes.put(key, value);
197         }
198
199         @Override
200         public Object getPluginAttribute(String key) {
201             return pluginAttributes.get(key);
202         }
203
204         @Override
205         public boolean isScriptless() {
206             return node.getChildInfo().isScriptless();
207         }
208
209         @Override
210         public boolean isConstantAttribute(String attribute) {
211             Node.JspAttribute attr = getNodeAttribute(attribute);
212             if (attr == null)
213                 return false;
214             return attr.isLiteral();
215         }
216
217         @Override
218         public String getConstantAttribute(String attribute) {
219             Node.JspAttribute attr = getNodeAttribute(attribute);
220             if (attr == null)
221                 return null;
222             return attr.getValue();
223         }
224
225         @Override
226         public boolean isAttributeSpecified(String attribute) {
227             return getNodeAttribute(attribute) != null;
228         }
229
230         @Override
231         public String getTemporaryVariableName() {
232             return node.getRoot().nextTemporaryVariableName();
233         }
234
235         @Override
236         public void generateImport(String imp) {
237             pageInfo.addImport(imp);
238         }
239
240         @Override
241         public void generateDeclaration(String id, String text) {
242             if (pageInfo.isPluginDeclared(id)) {
243                 return;
244             }
245             curNodes.add(new Node.Declaration(text, node.getStart(), null));
246         }
247
248         @Override
249         public void generateJavaSource(String sourceCode) {
250             curNodes.add(new Node.Scriptlet(sourceCode, node.getStart(),
251                     null));
252         }
253
254         @Override
255         public void generateAttribute(String attributeName) {
256             curNodes.add(new Node.AttributeGenerator(node.getStart(),
257                     attributeName,
258                     node));
259         }
260
261         @Override
262         public void dontUseTagPlugin() {
263             node.setUseTagPlugin(false);
264         }
265
266         @Override
267         public void generateBody() {
268             // Since we'll generate the body anyway, this is really a nop,
269             // except for the fact that it lets us put the Java sources the
270             // plugins produce in the correct order (w.r.t the body).
271             curNodes = node.getAtETag();
272         }
273
274         @Override
275         public boolean isTagFile() {
276             return pageInfo.isTagFile();
277         }
278
279         private Node.JspAttribute getNodeAttribute(String attribute) {
280             Node.JspAttribute[] attrs = node.getJspAttributes();
281             for (int i = 0; attrs != null && i < attrs.length; i++) {
282                 if (attrs[i].getName().equals(attribute)) {
283                     return attrs[i];
284                 }
285             }
286             return null;
287         }
288     }
289
290 }
291
292