1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.esigate.esi;
17
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.regex.Pattern;
25
26 import org.apache.http.client.methods.CloseableHttpResponse;
27 import org.esigate.ConfigurationException;
28 import org.esigate.Driver;
29 import org.esigate.DriverFactory;
30 import org.esigate.HttpErrorPage;
31 import org.esigate.Parameters;
32 import org.esigate.Renderer;
33 import org.esigate.http.HttpResponseUtils;
34 import org.esigate.impl.DriverRequest;
35 import org.esigate.parser.Adapter;
36 import org.esigate.parser.ElementType;
37 import org.esigate.parser.ParserContext;
38 import org.esigate.xml.XpathRenderer;
39 import org.esigate.xml.XsltRenderer;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 class IncludeElement extends BaseElement {
44 private static final String PROVIDER_PATTERN = "$(PROVIDER{";
45 private static final String LEGACY_PROVIDER_PATTERN = "$PROVIDER({";
46 private static final Logger LOG = LoggerFactory.getLogger(IncludeElement.class);
47 public static final ElementType TYPE = new BaseElementType("<esi:include", "</esi:include") {
48 @Override
49 public IncludeElement newInstance() {
50 return new IncludeElement();
51 }
52
53 };
54
55 private final Appendable outAdapter = new Adapter(IncludeElement.this);
56 private StringBuilder buf;
57 private Map<String, CharSequence> fragmentReplacements;
58 private Map<String, CharSequence> regexpReplacements;
59 private Tag includeTag;
60 private boolean write = false;
61
62 IncludeElement() {
63 }
64
65 @Override
66 public void characters(CharSequence csq, int start, int end) {
67 if (write) {
68 buf.append(csq, start, end);
69 }
70 }
71
72 @Override
73 public void onTagEnd(String tag, ParserContext ctx) throws IOException, HttpErrorPage {
74 write = true;
75 String src = includeTag.getAttribute("src");
76 String alt = includeTag.getAttribute("alt");
77 boolean ignoreError = "continue".equals(includeTag.getAttribute("onerror"));
78
79 Exception currentException = null;
80
81 try {
82 processPage(src, includeTag, ctx);
83 } catch (IOException | HttpErrorPage e) {
84 currentException = e;
85 } catch (ConfigurationException e) {
86
87 currentException = e;
88 LOG.error("Esi Include Tag with unknown Provider :" + e.getMessage());
89 }
90
91
92 if (currentException != null && alt != null) {
93
94 currentException = null;
95 try {
96 processPage(alt, includeTag, ctx);
97 } catch (IOException | HttpErrorPage e) {
98 currentException = e;
99 } catch (ConfigurationException e) {
100
101 currentException = e;
102 LOG.error("Esi Include Tag with unknown Provider :" + e.getMessage());
103 }
104 }
105
106
107 if (currentException != null && !ignoreError && !ctx.reportError(currentException)) {
108 if (currentException instanceof IOException) {
109 throw (IOException) currentException;
110 } else if (currentException instanceof HttpErrorPage) {
111 throw (HttpErrorPage) currentException;
112 } else if (currentException instanceof ConfigurationException) {
113 throw (ConfigurationException) currentException;
114 }
115 throw new IllegalStateException(
116 "This type of exception is unexpected here. Should be IOException or HttpErrorPageException or ConfigurationException.",
117 currentException);
118
119 }
120
121
122 if (!regexpReplacements.isEmpty()) {
123 for (Entry<String, CharSequence> entry : regexpReplacements.entrySet()) {
124 buf =
125 new StringBuilder(Pattern.compile(entry.getKey()).matcher(buf)
126 .replaceAll(entry.getValue().toString()));
127 }
128 }
129
130
131 ctx.getCurrent().characters(buf, 0, buf.length());
132 }
133
134 @Override
135 protected boolean parseTag(Tag tag, ParserContext ctx) {
136 buf = new StringBuilder(Parameters.DEFAULT_BUFFER_SIZE);
137 fragmentReplacements = new HashMap<>();
138 regexpReplacements = new HashMap<>();
139 includeTag = tag;
140 return true;
141 }
142
143 private void processPage(String src, Tag tag, ParserContext ctx) throws IOException, HttpErrorPage {
144 String fragment = tag.getAttribute("fragment");
145 String xpath = tag.getAttribute("xpath");
146 String xslt = tag.getAttribute("stylesheet");
147
148 DriverRequest httpRequest = ctx.getHttpRequest();
149 List<Renderer> rendererList = new ArrayList<>();
150 Driver driver;
151 String page;
152
153 int idx = src.indexOf(PROVIDER_PATTERN);
154 int idxLegacyPattern = src.indexOf(LEGACY_PROVIDER_PATTERN);
155 if (idx < 0 && idxLegacyPattern < 0) {
156 page = src;
157 driver = httpRequest.getDriver();
158 } else if (idx >= 0) {
159
160 int startIdx = idx + PROVIDER_PATTERN.length();
161 int endIndex = src.indexOf("})", startIdx);
162 String provider = src.substring(startIdx, endIndex);
163 page = src.substring(endIndex + "})".length());
164 driver = DriverFactory.getInstance(provider);
165 if (LOG.isWarnEnabled() && idx > 0) {
166 LOG.warn("Invalid src attribute : [{}], src should start with [{}{}})]."
167 + " First characters [{}] have been ignored", src, PROVIDER_PATTERN, provider,
168 src.substring(0, idx));
169 }
170
171 } else {
172 int startIdx = idxLegacyPattern + PROVIDER_PATTERN.length();
173 int endIndex = src.indexOf("})", startIdx);
174 String provider = src.substring(startIdx, endIndex);
175 page = src.substring(endIndex + "})".length());
176 driver = DriverFactory.getInstance(provider);
177 if (LOG.isWarnEnabled() && idxLegacyPattern > 0) {
178 LOG.warn("Invalid src attribute : [{}], src should start with [{}{}})]."
179 + " First characters [{}] have been ignored", src, PROVIDER_PATTERN, provider,
180 src.substring(0, idxLegacyPattern));
181 }
182 }
183
184 InlineCache ic = InlineCache.getFragment(src);
185 if (ic != null && !ic.isExpired()) {
186 String cache = ic.getFragment();
187 characters(cache, 0, cache.length());
188 } else {
189 EsiRenderer esiRenderer;
190 if (fragment != null) {
191 esiRenderer = new EsiRenderer(page, fragment);
192 } else {
193 esiRenderer = new EsiRenderer();
194 }
195 if (fragmentReplacements != null && !fragmentReplacements.isEmpty()) {
196 esiRenderer.setFragmentsToReplace(fragmentReplacements);
197 }
198 rendererList.add(esiRenderer);
199 if (xpath != null) {
200 rendererList.add(new XpathRenderer(xpath));
201 } else if (xslt != null) {
202 rendererList.add(new XsltRenderer(xslt, driver, httpRequest));
203 }
204 CloseableHttpResponse response =
205 driver.render(page, httpRequest.getOriginalRequest(),
206 rendererList.toArray(new Renderer[rendererList.size()]));
207 outAdapter.append(HttpResponseUtils.toString(response));
208 }
209 }
210
211 void addFragmentReplacement(String fragment, CharSequence replacement) {
212 fragmentReplacements.put(fragment, replacement);
213 }
214
215 void addRegexpReplacement(String regexp, CharSequence replacement) {
216 regexpReplacements.put(regexp, replacement);
217 }
218
219 }