/******************************************************************************* * Copyright (c) 2014-2016 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.properties.editor; import static org.springframework.ide.eclipse.boot.properties.editor.SpringPropertiesCompletionEngine.debug; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; import org.springframework.ide.eclipse.boot.util.FileUtil; /** * Load a {@link ConfigMetadataRepository} from the content of an eclipse * projects classpath. * * @author Kris De Volder */ public class StsConfigMetadataRepositoryJsonLoader { private static final String MAIN_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/spring-configuration-metadata.json"; public static final String ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/additional-spring-configuration-metadata.json"; /** * The default classpath location for config metadata loaded when scanning .jar files on the classpath. */ public static final String[] JAR_META_DATA_LOCATIONS = { MAIN_SPRING_CONFIGURATION_METADATA_JSON //Not scanning 'additional' metadata because it integrated already in the main data. }; /** * The default classpath location for config metadata loaded when scanning project output folders. */ public static final String[] PROJECT_META_DATA_LOCATIONS = { MAIN_SPRING_CONFIGURATION_METADATA_JSON, ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON }; private ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); /** * Load the {@link ConfigMetadataRepository} with the metadata of the current * classpath using the {@link #DEFAULT_LOCATION_PATTERN}. If the same config * metadata items is held within different resources, the first that is * loaded is kept which means the result is not deterministic. */ public ConfigurationMetadataRepository load(IJavaProject project) throws Exception { debug(">> load ConfigurationMetadataRepository for "+project.getElementName()); IClasspathEntry[] classpath = project.getResolvedClasspath(true); for (IClasspathEntry e : classpath) { int ekind = e.getEntryKind(); int ckind = e.getContentKind(); IPath path = e.getPath(); if (ekind==IClasspathEntry.CPE_LIBRARY && ckind==IPackageFragmentRoot.K_BINARY) { //jar file dependency File jarFile = path.toFile(); if (FileUtil.isJarFile(jarFile)) { loadFromJar(jarFile); } } else if (ekind==IClasspathEntry.CPE_PROJECT) { loadFromProjectDependency(e); } else { debug("Skipped: "+ekind(ekind)+" "+ckind(ckind)+": "+path); } } loadFromOutputFolder(project); ConfigurationMetadataRepository repository = builder.build(); debug("<< load ConfigurationMetadataRepository for "+project.getElementName()+": "+repository.getAllProperties().size()+" properties"); return repository; } private void loadFromProjectDependency(IClasspathEntry entry) { try { String pname = entry.getPath().segment(0); if (pname!=null) { IProject p = ResourcesPlugin.getWorkspace().getRoot().getProject(pname); if (p.isAccessible() && p.hasNature(JavaCore.NATURE_ID)) { loadFromOutputFolder(JavaCore.create(p)); } } } catch (Exception e) { SpringPropertiesEditorPlugin.log(e); } } private void loadFromOutputFolder(IJavaProject project) { try { IPath outputLoc = project.getOutputLocation(); if (outputLoc!=null) { IFolder outputFolder = ResourcesPlugin.getWorkspace().getRoot().getFolder(outputLoc); for (String mdLoc : PROJECT_META_DATA_LOCATIONS) { IFile mdf = outputFolder.getFile(new Path(mdLoc)); loadFromJsonFile(mdf); } } } catch (Exception e) { SpringPropertiesEditorPlugin.log(e); } } private void loadFromJsonFile(IFile mdf) { if (mdf.exists()) { InputStream is = null; try { is = mdf.getContents(true); loadFromInputStream(mdf, is); } catch (Exception e) { SpringPropertiesEditorPlugin.log(e); } finally { if (is!=null) { try { is.close(); } catch (IOException e) { //ignore } } } } } private void loadFromJar(File f) { debug("load from jar: "+f); JarFile jarFile = null; try { jarFile = new JarFile(f); //jarDump(jarFile); for (String loc : JAR_META_DATA_LOCATIONS) { ZipEntry e = jarFile.getEntry(loc); if (e!=null) { loadFrom(jarFile, e); } } } catch (Throwable e) { SpringPropertiesEditorPlugin.log(e); } finally { if (jarFile!=null) { try { jarFile.close(); } catch (IOException e) { } } } } private void loadFrom(JarFile jarFile, ZipEntry ze) { InputStream is = null; try { is = jarFile.getInputStream(ze); loadFromInputStream(jarFile.getName()+"["+ze.getName()+"]", is); } catch (Throwable e) { SpringPropertiesEditorPlugin.log(e); } finally { if (is!=null) { try { is.close(); } catch (IOException e) { } } } } private void loadFromInputStream(Object origin, InputStream is) throws IOException { builder.withJsonResource(origin, is); } /// Debug utils private String ckind(int ckind) { switch (ckind) { case IPackageFragmentRoot.K_SOURCE: return "SRC"; case IPackageFragmentRoot.K_BINARY: return "BIN"; default: return ""+ckind; } } private String ekind(int ekind) { switch (ekind) { case IClasspathEntry.CPE_SOURCE: return "SRC"; case IClasspathEntry.CPE_LIBRARY: return "LIB"; case IClasspathEntry.CPE_PROJECT: return "PRJ"; case IClasspathEntry.CPE_VARIABLE: return "VAR"; case IClasspathEntry.CPE_CONTAINER: return "CON"; default: return ""+ekind; } } // private void jarDump(JarFile jarFile) { // Enumeration<JarEntry> entries = jarFile.entries(); // while (entries.hasMoreElements()) { // JarEntry e = entries.nextElement(); // System.out.println(e.getName()); // } // } }