/* * Copyright (c) 2014-2015 the original author or authors * * 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 io.werval.doc; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import io.werval.api.Application; import io.werval.api.Config; import io.werval.api.outcomes.Outcome; import io.werval.controllers.Classpath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static io.werval.api.context.CurrentContext.application; import static io.werval.api.context.CurrentContext.mimeTypes; import static io.werval.api.context.CurrentContext.outcomes; import static io.werval.api.context.CurrentContext.reverseRoutes; import static io.werval.api.mime.MimeTypes.TEXT_HTML; import static io.werval.util.Charsets.UTF_8; import static io.werval.util.InputStreams.BUF_SIZE_4K; import static io.werval.util.InputStreams.readAllAsString; /** * Dynamic Documentations Controller. */ public class DynamicDocumentations { private static final Logger LOG = LoggerFactory.getLogger( DynamicDocumentations.class ); public Outcome index() throws IOException { Map<String, DynDoc> dyndocs = discoverDynDocs( application() ); String html = "<!doctype html><html><head><title>Dynamic Documentation</title></head>" + "<body><div id=\"header\"><h1>Dynamic Documentation</h1></div>" + "<div id=\"content\">" + "<p>Modules contains non-core functionnality. " + "Modules are simple JARs and can contain controllers, utility classes and Plugins.</p>" + "<ul>"; for( DynDoc dyndoc : dyndocs.values() ) { String dyndocUrl = reverseRoutes().get( getClass(), c -> c.resource( dyndoc.id, dyndoc.entryPoint ) ).httpUrl(); html += "<li><a href=\"" + dyndocUrl + "\">" + dyndoc.name + "</a></li>"; } html += "</ul></div><div id=\"footer\"></div></body></html>"; String decorated = SiteMeshHelper.decorate( reverseRoutes().get( getClass(), c -> c.index() ).uri(), html ); return outcomes().ok( decorated ).asHtml().build(); } public Outcome resource( String id, String path ) throws IOException { Map<String, DynDoc> dyndocs = discoverDynDocs( application() ); DynDoc dyndoc = dyndocs.get( id ); if( dyndoc == null ) { // Module not found return outcomes().notFound().asHtml().build(); } String resourcePath = dyndoc.basePath + "/" + path; if( application().classLoader().getResource( resourcePath ) == null ) { // Resource not found return outcomes().notFound().asHtml().build(); } String mimetype = mimeTypes().ofPath( resourcePath ); if( !TEXT_HTML.equals( mimetype ) ) { // Do not decorate non-HTML files return new Classpath().resource( resourcePath ); } String html = readAllAsString( application().classLoader().getResourceAsStream( resourcePath ), BUF_SIZE_4K, UTF_8 ); String decoratedHtml = SiteMeshHelper.decorate( reverseRoutes().get( getClass(), c -> c.resource( id, path ) ).uri(), html ); return outcomes().ok( decoratedHtml ).asHtml().build(); } private static final class DynDoc { private final String id; private final String basePath; private final String entryPoint; private final String name; private DynDoc( String id, String basePath, String entryPoint, String name ) { this.id = id; this.basePath = basePath; this.entryPoint = entryPoint; this.name = name; } @Override public String toString() { return "DynDoc{" + "id=" + id + ", basePath=" + basePath + ", entryPoint=" + entryPoint + ", name=" + name + '}'; } } private static Map<String, DynDoc> discoverDynDocs( Application application ) { Map<String, DynDoc> map = new LinkedHashMap<>(); Optional<Config> dyndocsConfig = application.config().atPathOptional( "werval.devshell.dyndocs" ); if( dyndocsConfig.isPresent() ) { for( String id : dyndocsConfig.get().subKeys() ) { Config dyndocConfig = dyndocsConfig.get().atKey( id ); if( !dyndocConfig.has( "base_path" ) ) { LOG.warn( "Dynamic Documentation for '{}' will not be registered as no 'base_path' is defined.", id ); break; } String basePath = dyndocConfig.string( "base_path" ); String entryPoint = dyndocConfig.stringOptional( "entry_point" ).orElse( "index.html" ); String entryPointResource = basePath + "/" + entryPoint; if( application.classLoader().getResource( entryPointResource ) == null ) { LOG.warn( "Dynamic Documentation for '{}' will not be served as '{}' can not be found.", id, entryPointResource ); break; } String name = dyndocConfig.stringOptional( "name" ).orElse( id ); map.put( id, new DynDoc( id, basePath, entryPoint, name ) ); } } return map; } }