/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.jooby.handlers;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.NoSuchElementException;
import org.jooby.Asset;
import org.jooby.Env;
import org.jooby.Request;
import org.jooby.Response;
import org.jooby.Route;
import com.google.common.io.CharStreams;
/**
* <h1>server side include</h1>
* <p>
* Custom {@link AssetHandler} with <code>server side include</code> function.
* </p>
*
* <h2>usage</h2>
*
* <pre>{@code
* {
* get("/static/**", new SSIHandler());
* }
* }</pre>
*
* <p>
* Request to <code>/static/index.html</code>:
* </p>
*
* <pre>
* <html>
* <-- /static/chunk.html -->
* </html>
* </pre>
*
* <p>
* The {@link SSIHandler} will resolve and insert the content of <code>/static/chunk.html</code>.
* </p>
*
* <h2>delimiters</h2>
* <p>
* Default delimiter are: <code><--</code> and <code>--></code>. You can override this using
* {@link #delimiters(String, String)} function:
* </p>
*
* <pre>{@code
* {
* get("/static/**", new SSIHandler().delimiters("{{", "}}");
* }
* }</pre>
*
* @author edgar
* @since 1.1.0
*/
public class SSIHandler extends AssetHandler {
private String startDelimiter = "<!--";
private String endDelimiter = "-->";
/**
* <p>
* Creates a new {@link SSIHandler}. The location pattern can be one of.
* </p>
*
* Given <code>/</code> like in <code>assets("/assets/**", "/")</code> with:
*
* <pre>
* GET /assets/js/index.js it translates the path to: /assets/js/index.js
* </pre>
*
* Given <code>/assets</code> like in <code>assets("/js/**", "/assets")</code> with:
*
* <pre>
* GET /js/index.js it translate the path to: /assets/js/index.js
* </pre>
*
* Given <code>/META-INF/resources/webjars/{0}</code> like in
* <code>assets("/webjars/**", "/META-INF/resources/webjars/{0}")</code> with:
*
* <pre>
* GET /webjars/jquery/2.1.3/jquery.js it translate the path to: /META-INF/resources/webjars/jquery/2.1.3/jquery.js
* </pre>
*
* @param pattern Pattern to locate static resources.
*/
public SSIHandler(final String pattern) {
super(pattern);
}
/**
* <p>
* Creates a new {@link SSIHandler}. Location pattern is set to: <code>/</code>.
* </p>
*/
public SSIHandler() {
this("/");
}
/**
* Set/override delimiters.
*
* @param start Start delimiter.
* @param end Stop/end delimiter.
* @return This handler.
*/
public SSIHandler delimiters(final String start, final String end) {
this.startDelimiter = start;
this.endDelimiter = end;
return this;
}
@Override
protected void send(final Request req, final Response rsp, final Asset asset) throws Throwable {
Env env = req.require(Env.class);
CharSequence text = process(env, text(asset.stream()));
rsp.type(asset.type())
.send(text);
}
private String process(final Env env, final String src) {
return env.resolver()
.delimiters(startDelimiter, endDelimiter)
.source(key -> process(env, file(key)))
.ignoreMissing()
.resolve(src);
}
private String file(final String key) {
String file = Route.normalize(key.trim());
return text(getClass().getResourceAsStream(file));
}
private String text(final InputStream stream) {
try (InputStream in = stream) {
return CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
} catch (IOException | NullPointerException x) {
throw new NoSuchElementException();
}
}
}