IncludeElement.java
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.esigate.esi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.esigate.ConfigurationException;
import org.esigate.Driver;
import org.esigate.DriverFactory;
import org.esigate.HttpErrorPage;
import org.esigate.Parameters;
import org.esigate.Renderer;
import org.esigate.http.HttpResponseUtils;
import org.esigate.impl.DriverRequest;
import org.esigate.parser.Adapter;
import org.esigate.parser.ElementType;
import org.esigate.parser.ParserContext;
import org.esigate.xml.XpathRenderer;
import org.esigate.xml.XsltRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class IncludeElement extends BaseElement {
private static final String PROVIDER_PATTERN = "$(PROVIDER{";
private static final String LEGACY_PROVIDER_PATTERN = "$PROVIDER({";
private static final Logger LOG = LoggerFactory.getLogger(IncludeElement.class);
public static final ElementType TYPE = new BaseElementType("<esi:include", "</esi:include") {
@Override
public IncludeElement newInstance() {
return new IncludeElement();
}
};
private final Appendable outAdapter = new Adapter(IncludeElement.this);
private StringBuilder buf;
private Map<String, CharSequence> fragmentReplacements;
private Map<String, CharSequence> regexpReplacements;
private Tag includeTag;
private boolean write = false;
IncludeElement() {
}
@Override
public void characters(CharSequence csq, int start, int end) {
if (write) {
buf.append(csq, start, end);
}
}
@Override
public void onTagEnd(String tag, ParserContext ctx) throws IOException, HttpErrorPage {
write = true;
String src = includeTag.getAttribute("src");
String alt = includeTag.getAttribute("alt");
boolean ignoreError = "continue".equals(includeTag.getAttribute("onerror"));
Exception currentException = null;
// Handle src
try {
processPage(src, includeTag, ctx);
} catch (IOException | HttpErrorPage e) {
currentException = e;
} catch (ConfigurationException e) {
// case uknown provider : log error
currentException = e;
LOG.error("Esi Include Tag with unknown Provider :" + e.getMessage());
}
// Handle Alt
if (currentException != null && alt != null) {
// Reset exception
currentException = null;
try {
processPage(alt, includeTag, ctx);
} catch (IOException | HttpErrorPage e) {
currentException = e;
} catch (ConfigurationException e) {
// case uknown provider : log error
currentException = e;
LOG.error("Esi Include Tag with unknown Provider :" + e.getMessage());
}
}
// Handle onerror
if (currentException != null && !ignoreError && !ctx.reportError(currentException)) {
if (currentException instanceof IOException) {
throw (IOException) currentException;
} else if (currentException instanceof HttpErrorPage) {
throw (HttpErrorPage) currentException;
} else if (currentException instanceof ConfigurationException) {
throw (ConfigurationException) currentException;
}
throw new IllegalStateException(
"This type of exception is unexpected here. Should be IOException or HttpErrorPageException or ConfigurationException.",
currentException);
}
// apply regexp replacements
if (!regexpReplacements.isEmpty()) {
for (Entry<String, CharSequence> entry : regexpReplacements.entrySet()) {
buf =
new StringBuilder(Pattern.compile(entry.getKey()).matcher(buf)
.replaceAll(entry.getValue().toString()));
}
}
// write accumulated data into parent
ctx.getCurrent().characters(buf, 0, buf.length());
}
@Override
protected boolean parseTag(Tag tag, ParserContext ctx) {
buf = new StringBuilder(Parameters.DEFAULT_BUFFER_SIZE);
fragmentReplacements = new HashMap<>();
regexpReplacements = new HashMap<>();
includeTag = tag;
return true;
}
private void processPage(String src, Tag tag, ParserContext ctx) throws IOException, HttpErrorPage {
String fragment = tag.getAttribute("fragment");
String xpath = tag.getAttribute("xpath");
String xslt = tag.getAttribute("stylesheet");
DriverRequest httpRequest = ctx.getHttpRequest();
List<Renderer> rendererList = new ArrayList<>();
Driver driver;
String page;
int idx = src.indexOf(PROVIDER_PATTERN);
int idxLegacyPattern = src.indexOf(LEGACY_PROVIDER_PATTERN);
if (idx < 0 && idxLegacyPattern < 0) {
page = src;
driver = httpRequest.getDriver();
} else if (idx >= 0) {
int startIdx = idx + PROVIDER_PATTERN.length();
int endIndex = src.indexOf("})", startIdx);
String provider = src.substring(startIdx, endIndex);
page = src.substring(endIndex + "})".length());
driver = DriverFactory.getInstance(provider);
if (LOG.isWarnEnabled() && idx > 0) {
LOG.warn("Invalid src attribute : [{}], src should start with [{}{}})]."
+ " First characters [{}] have been ignored", src, PROVIDER_PATTERN, provider,
src.substring(0, idx));
}
} else {
int startIdx = idxLegacyPattern + PROVIDER_PATTERN.length();
int endIndex = src.indexOf("})", startIdx);
String provider = src.substring(startIdx, endIndex);
page = src.substring(endIndex + "})".length());
driver = DriverFactory.getInstance(provider);
if (LOG.isWarnEnabled() && idxLegacyPattern > 0) {
LOG.warn("Invalid src attribute : [{}], src should start with [{}{}})]."
+ " First characters [{}] have been ignored", src, PROVIDER_PATTERN, provider,
src.substring(0, idxLegacyPattern));
}
}
InlineCache ic = InlineCache.getFragment(src);
if (ic != null && !ic.isExpired()) {
String cache = ic.getFragment();
characters(cache, 0, cache.length());
} else {
EsiRenderer esiRenderer;
if (fragment != null) {
esiRenderer = new EsiRenderer(page, fragment);
} else {
esiRenderer = new EsiRenderer();
}
if (fragmentReplacements != null && !fragmentReplacements.isEmpty()) {
esiRenderer.setFragmentsToReplace(fragmentReplacements);
}
rendererList.add(esiRenderer);
if (xpath != null) {
rendererList.add(new XpathRenderer(xpath));
} else if (xslt != null) {
rendererList.add(new XsltRenderer(xslt, driver, httpRequest));
}
CloseableHttpResponse response =
driver.render(page, httpRequest.getOriginalRequest(),
rendererList.toArray(new Renderer[rendererList.size()]));
outAdapter.append(HttpResponseUtils.toString(response));
}
}
void addFragmentReplacement(String fragment, CharSequence replacement) {
fragmentReplacements.put(fragment, replacement);
}
void addRegexpReplacement(String regexp, CharSequence replacement) {
regexpReplacements.put(regexp, replacement);
}
}