/** * 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(); } } }