/*
* Copyright 2012-2017 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 org.springframework.boot.actuate.endpoint.mvc;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.resource.ResourceTransformer;
import org.springframework.web.servlet.resource.ResourceTransformerChain;
import org.springframework.web.servlet.resource.TransformedResource;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
/**
* {@link MvcEndpoint} to expose a HAL browser.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 1.3.0
*/
public class HalBrowserMvcEndpoint extends HalJsonMvcEndpoint
implements ResourceLoaderAware {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static HalBrowserLocation[] HAL_BROWSER_RESOURCE_LOCATIONS = {
new HalBrowserLocation("classpath:/META-INF/spring-data-rest/hal-browser/",
"index.html"),
new HalBrowserLocation(
"classpath:/META-INF/resources/webjars/hal-browser/3325375/",
"browser.html") };
private HalBrowserLocation location;
public HalBrowserMvcEndpoint(ManagementServletContext managementServletContext) {
super(managementServletContext);
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public String browse(HttpServletRequest request) {
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder
.fromRequest(request);
String uriString = builder.build().toUriString();
return "redirect:" + uriString + (uriString.endsWith("/") ? "" : "/")
+ this.location.getHtmlFile();
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.location = getHalBrowserLocation(resourceLoader);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Make sure the root path is not cached so the browser comes back for the JSON
// and add a transformer to set the initial link
if (this.location != null) {
String start = getManagementServletContext().getContextPath() + getPath();
registry.addResourceHandler(start + "/", start + "/**")
.addResourceLocations(this.location.getResourceLocation())
.setCachePeriod(0).resourceChain(true)
.addTransformer(new InitialUrlTransformer());
}
}
public static HalBrowserLocation getHalBrowserLocation(
ResourceLoader resourceLoader) {
for (HalBrowserLocation candidate : HAL_BROWSER_RESOURCE_LOCATIONS) {
try {
Resource resource = resourceLoader.getResource(candidate.toString());
if (resource != null && resource.exists()) {
return candidate;
}
}
catch (Exception ex) {
// Ignore
}
}
return null;
}
/**
* HAL Browser properties.
*/
public static class HalBrowserLocation {
private final String resourceLocation;
private final String htmlFile;
public HalBrowserLocation(String resourceLocation, String html) {
this.resourceLocation = resourceLocation;
this.htmlFile = html;
}
public String getResourceLocation() {
return this.resourceLocation;
}
public String getHtmlFile() {
return this.htmlFile;
}
@Override
public String toString() {
return this.resourceLocation + this.htmlFile;
}
}
/**
* {@link ResourceTransformer} to change the initial link location.
*/
private class InitialUrlTransformer implements ResourceTransformer {
@Override
public Resource transform(HttpServletRequest request, Resource resource,
ResourceTransformerChain transformerChain) throws IOException {
resource = transformerChain.transform(request, resource);
if (resource.getFilename().equalsIgnoreCase(
HalBrowserMvcEndpoint.this.location.getHtmlFile())) {
return replaceInitialLink(request, resource);
}
return resource;
}
private Resource replaceInitialLink(HttpServletRequest request, Resource resource)
throws IOException {
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
String content = new String(bytes, DEFAULT_CHARSET);
List<String> pathSegments = new ArrayList<>(ServletUriComponentsBuilder
.fromRequest(request).build().getPathSegments());
pathSegments.remove(pathSegments.size() - 1);
String initial = "/"
+ StringUtils.collectionToDelimitedString(pathSegments, "/");
content = content.replace("entryPoint: '/'", "entryPoint: '" + initial + "'");
return new TransformedResource(resource, content.getBytes(DEFAULT_CHARSET));
}
}
}