/*
* #%L
* ACS AEM Tools Bundle
* %%
* Copyright (C) 2013 Adobe
* %%
* 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.
* #L%
*/
package com.adobe.acs.livereload.impl;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
public final class JavaScriptInjectionFilter implements Filter {
private class BufferingResponse extends SlingHttpServletResponseWrapper {
private StringWriter stringWriter;
public BufferingResponse(final SlingHttpServletResponse slingResponse) {
super(slingResponse);
}
public String getContents() {
if (this.stringWriter != null) {
return this.stringWriter.toString();
}
return null;
}
public PrintWriter getWriter() throws IOException {
if (stringWriter == null) {
stringWriter = new StringWriter();
}
return new PrintWriter(stringWriter);
}
@Override
public void resetBuffer() {
if (this.stringWriter != null) {
this.stringWriter = new StringWriter();
}
super.resetBuffer();
}
}
private int port;
private String[] prefixes;
public JavaScriptInjectionFilter(int port, String[] prefixes) {
this.port = port;
this.prefixes = prefixes;
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
if (!(servletRequest instanceof SlingHttpServletRequest)
|| !(servletResponse instanceof SlingHttpServletResponse)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) servletRequest;
final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) servletResponse;
if (!this.accepts(slingRequest)) {
filterChain.doFilter(slingRequest, slingResponse);
return;
}
final BufferingResponse capturedResponse = new BufferingResponse(slingResponse);
filterChain.doFilter(slingRequest, capturedResponse);
// Get contents
final String contents = capturedResponse.getContents();
if (contents != null) {
if (StringUtils.contains(slingResponse.getContentType(), "html")) {
final int bodyIndex = contents.indexOf("</body>");
if (bodyIndex != -1) {
final PrintWriter printWriter = slingResponse.getWriter();
printWriter.write(contents.substring(0, bodyIndex));
printWriter.write(String.format(
"<script type=\"text/javascript\" src=\"http://%s:%s/livereload.js\"></script>",
slingRequest.getServerName(), port));
printWriter.write(contents.substring(bodyIndex));
return;
}
}
}
if (contents != null) {
slingResponse.getWriter().write(contents);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
private boolean accepts(final SlingHttpServletRequest slingRequest) {
final Resource resource = slingRequest.getResource();
if (!StringUtils.equalsIgnoreCase("get", slingRequest.getMethod())) {
// Only inject on GET requests
return false;
} else if (!StringUtils.startsWithAny(resource.getPath(), prefixes)) {
return false;
} else if (StringUtils.equals(slingRequest.getHeader("X-Requested-With"), "XMLHttpRequest")) {
// Do not inject into XHR requests
return false;
}
return true;
}
}