com.netflix.exhibitor.core.rest.UIResource Maven / Gradle / Ivy
/*
* Copyright 2012 Netflix, Inc.
*
* 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 com.netflix.exhibitor.core.rest;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Resources;
import com.netflix.exhibitor.core.backup.BackupConfigSpec;
import com.netflix.exhibitor.core.config.EncodedConfigParser;
import com.netflix.exhibitor.core.entities.UITabSpec;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Path("exhibitor/v1/ui")
public class UIResource
{
private final UIContext context;
private final List tabs;
private static final Logger log = LoggerFactory.getLogger(UIResource.class);
private static final FileTypeMap fileTypeMap;
static
{
MimetypesFileTypeMap localFileTypeMap = null;
URL url = Resources.getResource("mimetypes.default");
if ( url != null )
{
InputStream stream = null;
try
{
stream = url.openStream();
localFileTypeMap = new MimetypesFileTypeMap(stream);
}
catch ( IOException e )
{
log.error("Error loading mimetypes.default", e);
}
finally
{
try
{
if ( stream != null )
{
stream.close();
}
}
catch ( IOException e )
{
// ignore
}
}
}
else
{
log.error("Could not find mimetypes.default");
}
fileTypeMap = (localFileTypeMap != null) ? localFileTypeMap : MimetypesFileTypeMap.getDefaultFileTypeMap();
}
private static final String jQueryUiPrefix = "css/jquery/";
private static final String TEXT_UI_TAB_BASE_URL = "tab/";
private static final String HTML_UI_TAB_BASE_URL = "tab-html/";
public UIResource(@Context ContextResolver< UIContext > resolver)
{
context = resolver.getContext(UIContext.class);
tabs = buildTabs();
}
@Path("{file:.*}")
@GET
public Response getResource(@PathParam("file") String fileName) throws IOException
{
if ( fileName.startsWith(jQueryUiPrefix) )
{
String stripped = fileName.substring(jQueryUiPrefix.length());
fileName = "css/jquery/" + context.getExhibitor().getJQueryStyle().name().toLowerCase() + "/" + stripped;
}
URL resource;
try
{
resource = Resources.getResource("com/netflix/exhibitor/core/ui/" + fileName);
}
catch ( IllegalArgumentException dummy )
{
return Response.status(Response.Status.NOT_FOUND).build();
}
String resourceFile = resource.getFile();
String contentType;
if ( resourceFile.endsWith(".png") )
{
contentType = "image/png"; // not in default mime types
}
else if ( resourceFile.endsWith(".js") )
{
contentType = "text/javascript"; // not in default mime types
}
else if ( resourceFile.endsWith(".css") )
{
contentType = "text/css"; // not in default mime types
}
else
{
contentType = fileTypeMap.getContentType(resourceFile);
}
Object entity;
if ( contentType.startsWith("text/") )
{
entity = Resources.toString(resource, Charset.forName("UTF-8"));
}
else
{
entity = Resources.toByteArray(resource);
}
return Response.ok(entity).type(contentType).build();
}
@Path("tabs")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAdditionalTabSpecs()
{
final AtomicInteger index = new AtomicInteger(0);
List names = Lists.transform
(
tabs,
new Function()
{
@Override
public UITabSpec apply(UITab tab)
{
String base = tab.contentIsHtml() ? HTML_UI_TAB_BASE_URL : TEXT_UI_TAB_BASE_URL;
return new UITabSpec(tab.getName(), base + index.getAndIncrement(), tab.contentIsHtml(), tab.getUITabType());
}
}
);
names = Lists.newArrayList(names); // move out of Google's TransformingRandomAccessList
GenericEntity> entity = new GenericEntity>(names){};
return Response.ok(entity).build();
}
@Path("tab-html/{index}")
@GET
@Produces(MediaType.TEXT_HTML)
public Response getAdditionalTabContent(@Context UriInfo info, @PathParam("index") int index) throws Exception
{
return getAdditionalTabHtmlContent(info, index);
}
@Path("tab/{index}")
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response getAdditionalTabHtmlContent(@Context UriInfo info, @PathParam("index") int index) throws Exception
{
if ( (index < 0) || (index >= tabs.size()) )
{
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(tabs.get(index).getContent(info)).build();
}
@Path("backup-config")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getBackupConfig() throws Exception
{
ArrayNode node = JsonNodeFactory.instance.arrayNode();
if ( context.getExhibitor().getBackupManager().isActive() )
{
EncodedConfigParser parser = context.getExhibitor().getBackupManager().getBackupConfigParser();
List configs = context.getExhibitor().getBackupManager().getConfigSpecs();
for ( BackupConfigSpec c : configs )
{
ObjectNode n = JsonNodeFactory.instance.objectNode();
String value = parser.getValue(c.getKey());
n.put("key", c.getKey());
n.put("name", c.getDisplayName());
n.put("help", c.getHelpText());
n.put("value", (value != null) ? value : "");
n.put("type", c.getType().name().toLowerCase().substring(0, 1));
node.add(n);
}
}
return JsonUtil.writeValueAsString(node);
}
@Path("shutdown")
@GET
public Response shutdown()
{
Runnable shutdownProc = context.getExhibitor().getShutdownProc();
if ( shutdownProc == null )
{
return Response.status(Response.Status.NOT_FOUND).build();
}
shutdownProc.run();
return Response.ok().build();
}
static String getLog(UIContext context)
{
List log = context.getExhibitor().getLog().toDisplayList("\t", context.getExhibitor().getLogDirection());
StringBuilder str = new StringBuilder();
for ( String s : log )
{
str.append(s).append("\n");
}
return str.toString();
}
private ImmutableList buildTabs()
{
ImmutableList.Builder builder = ImmutableList.builder();
builder.add
(
new UITabImpl("Log")
{
@Override
public String getContent(UriInfo info) throws Exception
{
return getLog(context);
}
}
);
Collection additionalUITabs = context.getExhibitor().getAdditionalUITabs();
if ( additionalUITabs != null )
{
builder.addAll(additionalUITabs);
}
return builder.build();
}
static String fixName(Enum c)
{
StringBuilder str = new StringBuilder();
String[] parts = c.name().toLowerCase().split("_");
for ( String p : parts )
{
if ( p.length() > 0 )
{
if ( str.length() > 0 )
{
str.append(p.substring(0, 1).toUpperCase());
if ( p.length() > 1 )
{
str.append(p.substring(1));
}
}
else
{
str.append(p);
}
}
}
return str.toString();
}
}