package com.dianping.pigeon.console.servlet.json;
import com.dianping.pigeon.config.ConfigManager;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.console.domain.MavenCoordinate;
import com.dianping.pigeon.console.domain.ServicePath;
import com.dianping.pigeon.console.domain.ServicePaths;
import com.dianping.pigeon.console.servlet.ServiceServlet;
import com.dianping.pigeon.remoting.invoker.config.InvokerConfig;
import com.dianping.pigeon.remoting.provider.config.ProviderConfig;
import com.dianping.pigeon.remoting.provider.config.ServerConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Created by shihuashen on 16/10/24.
*/
public class JarJsonServlet extends ServiceServlet {
private static ConcurrentMap<String, ServicePath> pathMap = new ConcurrentHashMap<String, ServicePath>();
private static ConcurrentMap<String, List<MavenCoordinate>> jarMap = new ConcurrentHashMap<String, List<MavenCoordinate>>();
private static ConfigManager configManager = ConfigManagerLoader.getConfigManager();
public JarJsonServlet(ServerConfig serverConfig, int port) {
super(serverConfig, port);
}
private List<ServicePath> getProviderServicePaths() {
List<ServicePath> paths = new LinkedList<>();
Map<String, ProviderConfig<?>> serviceProviders = getServiceProviders();
for (Map.Entry<String, ProviderConfig<?>> entry : serviceProviders.entrySet()) {
String serviceName = entry.getKey();
ProviderConfig<?> providerConfig = entry.getValue();
Class<?> serviceInterface = providerConfig.getServiceInterface();
paths.add(getServicePath(serviceName,serviceInterface));
}
return paths;
}
private List<ServicePath> getInvokerServicePaths(){
List<ServicePath> paths = new LinkedList<>();
Set<InvokerConfig<?>> invokerConfigs = getInvokerConfigs().keySet();
for(InvokerConfig<?> invokerConfig : invokerConfigs){
String serviceName = invokerConfig.getUrl();
Class<?> serviceInterface = invokerConfig.getServiceInterface();
paths.add(getServicePath(serviceName,serviceInterface));
}
return paths;
}
private ServicePath getServicePath(String serviceName,Class<?> serviceInterface){
ServicePath servicePath = pathMap.get(serviceName);
if(servicePath == null){
servicePath = new ServicePath();
servicePath.setService(serviceName);
servicePath.setGroup(configManager.getGroup());
URL url = serviceInterface.getResource(serviceInterface.getSimpleName() + ".class");
if (url != null) {
String path = url.getFile();
servicePath.setPath(trimPath(path));
}
MavenCoordinate coordinate = getCoordinate(serviceInterface);
if (coordinate != null) {
servicePath.setGroupId(coordinate.getGroupId());
servicePath.setArtifactId(coordinate.getArtifactId());
servicePath.setVersion(coordinate.getVersion());
servicePath.setTime(coordinate.getTime());
}
pathMap.put(serviceName, servicePath);
}
return servicePath;
}
@Override
protected boolean initServicePage(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServicePaths paths = new ServicePaths();
paths.setInvokerPaths(getInvokerServicePaths());
paths.setProviderPaths(getProviderServicePaths());
this.model = paths;
return true;
}
@Override
public String getView() {
return "Jars.ftl";
}
@Override
public String getContentType() {
return "application/json; charset=UTF-8";
}
private MavenCoordinate getCoordinate(Class<?> serviceInterface) {
// MavenCoordinate coordinate = null;
// coordinate = getFromManifest(serviceInterface);
// if(coordinate!=null)
// return coordinate;
return detect(serviceInterface);
}
private MavenCoordinate getFromManifest(Class<?> serviceInterface) {
MavenCoordinate mavenCoordinate = new MavenCoordinate();
Package aPackage = serviceInterface.getPackage();
String artifactId = aPackage.getImplementationTitle();
String groupId = aPackage.getImplementationVendor();
String version = aPackage.getImplementationVersion();
mavenCoordinate.setArtifactId(artifactId);
mavenCoordinate.setGroupId(groupId);
mavenCoordinate.setVersion(version);
boolean validated = false;
try {
validated = validate(mavenCoordinate, serviceInterface);
} catch (IOException e) {
logger.info(e);
}
if (validated)
return mavenCoordinate;
else
return null;
}
private MavenCoordinate detect(Class<?> serviceInterface) {
String jarFilePath = null;
try {
jarFilePath = serviceInterface.getProtectionDomain().getCodeSource().getLocation().getFile();
} catch (SecurityException e) {
logger.info("Permission deny " + e);
} catch (NullPointerException e) {
logger.info("Null pointer in get jar path");
} catch (Throwable t) {
logger.info(t);
}
if (jarFilePath == null)
return null;
List<MavenCoordinate> coordinates = jarMap.get(jarFilePath);
if (coordinates == null) {
coordinates = new LinkedList<MavenCoordinate>();
JarFile jarFile = null;
try {
jarFile = new JarFile(jarFilePath);
Enumeration<JarEntry> files = jarFile.entries();
while (files.hasMoreElements()) {
JarEntry entry = files.nextElement();
if (entry.getName().endsWith("pom.properties")) {
MavenCoordinate coordinate = new MavenCoordinate();
InputStream in = null;
try {
in = serviceInterface.getClassLoader().getResourceAsStream(entry.getName());
Properties p = new Properties();
p.load(in);
coordinate.setVersion(p.getProperty("version"));
coordinate.setArtifactId(p.getProperty("artifactId"));
coordinate.setGroupId(p.getProperty("groupId"));
} catch (IOException e) {
logger.info(e);
break;
} finally {
if (in != null) {
try{
in.close();
}catch (IOException e){
logger.info(e);
}
}
}
BufferedReader reader = null;
try {
in = serviceInterface.getClassLoader().getResourceAsStream(entry.getName());
reader = new BufferedReader(new InputStreamReader(in));
reader.readLine();
coordinate.setTime(reader.readLine());
in.close();
} catch (IOException e) {
logger.info(e);
break;
} finally {
if (reader != null) {
try{
reader.close();
}catch (IOException e){
logger.info(e);
}
}
if (in != null) {
try{
in.close();
}catch (IOException e){
logger.info(e);
}
}
}
coordinates.add(coordinate);
}
}
jarMap.put(jarFilePath, coordinates);
} catch (IOException e) {
logger.info(e);
return null;
}finally {
if(jarFile!=null)
try {
jarFile.close();
} catch (IOException e) {
logger.info(e);
}
}
}
for (MavenCoordinate coordinate : coordinates) {
String jarName = coordinate.getArtifactId() + "-" + coordinate.getVersion() + ".jar";
String actualJarName = getJarName(jarFilePath);
if (jarName.equals(actualJarName)) {
return coordinate;
}
}
return null;
}
private boolean validate(MavenCoordinate mavenCoordinate, Class<?> serviceInterface) throws IOException {
if (mavenCoordinate.getArtifactId() != null && mavenCoordinate.getGroupId() != null) {
InputStream in = serviceInterface.getClassLoader().getResourceAsStream("META-INF/maven." + mavenCoordinate.getGroupId() + "." + mavenCoordinate.getArtifactId() + "/pom.properties");
Properties p = new Properties();
p.load(in);
String version = p.getProperty("version");
String groupId = p.getProperty("groupId");
String artifactId = p.getProperty("artifactId");
in.close();
if (groupId.equals(mavenCoordinate.getGroupId())
&& artifactId.equals(mavenCoordinate.getArtifactId())) {
in = serviceInterface.getClassLoader().getResourceAsStream("META-INF/maven." + mavenCoordinate.getGroupId() + "." + mavenCoordinate.getArtifactId() + "/pom.properties");
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
reader.readLine();
String time = reader.readLine();
reader.close();
in.close();
mavenCoordinate.setTime(time);
mavenCoordinate.setVersion(version);
return true;
}
return false;
}
return false;
}
private String getJarName(String jarFilePath) {
String[] strs = jarFilePath.split("/");
if (strs.length != 0) {
return strs[strs.length - 1];
}
return null;
}
private String trimPath(String path) {
int index = path.indexOf("!");
String jarPath = path.substring(0, index);
String[] strs = jarPath.split("/");
if (strs.length > 0) {
String jarName = strs[strs.length - 1];
return jarName + path.substring(index);
}
return null;
}
}