/*
* Copyright 2013 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 netflix.adminresources.resources;
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.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.annotations.Beta;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author pkamath
* @author Nitesh Kant
*/
@Beta
@Path("/jars")
@Produces(MediaType.APPLICATION_JSON)
public class JarsInfoResource {
private static final Logger logger = LoggerFactory.getLogger(JarsInfoResource.class);
private static final String JAR_PATTERN = "^jar:file:(.+)!/META-INF/MANIFEST.MF$";
private final List<JarManifest> jarManifests;
private final ArrayList<JarInfo> jarInfos;
public JarsInfoResource() {
jarManifests = loadJarManifests();
jarInfos = new ArrayList<>();
for (JarManifest jm : jarManifests) {
jarInfos.add(jm.toJarInfo());
}
}
public static class JarsInfoResponse {
private List<JarInfo> jars;
public JarsInfoResponse(List<JarInfo> jarInfos) {
this.jars = jarInfos;
}
public List<JarInfo> getJars() {
return jars;
}
}
@GET
public Response getAllJarsInfo() {
GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls();
Gson gson = gsonBuilder.create();
String jarsJson = gson.toJson(new JarsInfoResponse(jarInfos));
return Response.ok(jarsJson).build();
}
@GET
@Path("/{id}")
public Response getJarManifest(@PathParam("id") int jarId) {
GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls();
Gson gson = gsonBuilder.create();
String propsJson = gson.toJson(new KaryonAdminResponse(jarManifests.get(jarId)));
return Response.ok(propsJson).build();
}
private static List<JarManifest> loadJarManifests() {
List<JarManifest> jarManifests = new ArrayList<>();
Pattern pattern = Pattern.compile(JAR_PATTERN);
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Enumeration<URL> urls = cl.getResources("META-INF/MANIFEST.MF");
int id = 0;
while (urls.hasMoreElements()) {
URL manifestURL = urls.nextElement();
InputStream is = manifestURL.openStream();
String key = manifestURL.toString();
Matcher matcher = pattern.matcher(key);
if (matcher.matches()) {
key = matcher.group(1);
}
jarManifests.add(new JarManifest(id, key, new Manifest(is)));
is.close();
id++;
}
} catch (Exception e) {
logger.error("Failed to load environment jar information.", e);
}
return jarManifests;
}
private static class JarManifest {
private final int id;
private final String jarName;
private final Manifest manifest;
private JarManifest(int id, String jarName, Manifest manifest) {
this.id = id;
this.jarName = jarName;
this.manifest = manifest;
}
public String getJarName() {
return jarName;
}
public Manifest getManifest() {
return manifest;
}
public JarInfo toJarInfo() {
return new JarInfo(id, jarName, manifest.getMainAttributes());
}
}
private static class JarInfo {
public static final String LIBRARY_OWNER = "Library-Owner";
public static final String BUILD_DATE = "Build-Date";
public static final String STATUS = "Status";
public static final String IMPLEMENTATION_VERSION = "Implementation-Version";
public static final String IMPLEMENTATION_TITLE = "Implementation-Title";
public static final String SPECIFICATION_VERSION = "Specification-Version";
public static final String UNAVAILABLE = "-";
private final int id;
private final String name;
private final String libraryOwner;
private final String buildDate;
private final String status;
private final String implementationVersion;
private final String implementationTitle;
private final String specificationVersion;
private JarInfo(int id, String jar, Attributes mainAttributes) {
this.id = id;
this.name = jar;
libraryOwner = valueOf(mainAttributes, LIBRARY_OWNER);
buildDate = valueOf(mainAttributes, BUILD_DATE);
status = valueOf(mainAttributes, STATUS);
implementationTitle = valueOf(mainAttributes, IMPLEMENTATION_TITLE);
implementationVersion = valueOf(mainAttributes, IMPLEMENTATION_VERSION);
specificationVersion = valueOf(mainAttributes, SPECIFICATION_VERSION);
}
public String getStatus() {
return status;
}
public String getLibraryOwner() {
return libraryOwner;
}
public String getBuildDate() {
return buildDate;
}
public String getName() {
return name;
}
public String getImplementationVersion() {
return implementationVersion;
}
public String getImplementationTitle() {
return implementationTitle;
}
public String getSpecificationVersion() {
return specificationVersion;
}
private static String valueOf(Attributes mainAttributes, String tag) {
String value = mainAttributes.getValue(tag);
return value == null ? UNAVAILABLE : value;
}
}
}