/******************************************************************************* * Copyright (c) 2012 Pivotal Software, 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 Software, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.core.internal.model; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.util.LinkedHashSet; import java.util.Properties; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.IVMInstall2; import org.grails.ide.eclipse.core.GrailsCoreActivator; import org.grails.ide.eclipse.core.model.GrailsVersion; import org.grails.ide.eclipse.core.model.IGrailsInstall; import org.osgi.framework.Constants; import org.osgi.framework.Version; /** * @author Christian Dupuis * @author Nieraj Singh * @author Kris De Volder */ public class DefaultGrailsInstall implements IGrailsInstall { private static final String UNKNOWN_VERSION_STRING = "<unknown>"; private static String defaultGrailsWorkDir = null; /** * If this is set to a non-null value then it will be used to initialize * each instance of DefaultGrailsInstall created. This is mainly useful for * testing purposes, to ensure that when running JUnit tests, the ".grails" * folder is where we want it and can be easily cleared before running * tests. */ public static void setDefaultGrailsWorkDir(String value) { Assert.isLegal( value == null || !value.contains(" "), "Grails commandLine parser will get confused by spaces. Paths with ' ' in them are not allowed!"); defaultGrailsWorkDir = value; } public static String getDefaultGrailsWorkDir() { return defaultGrailsWorkDir; } private final String home; private boolean isDefault = false; private final String name; private SpringloadedJarFinder loadedJarFinder = new SpringloadedJarFinder(); public DefaultGrailsInstall(String home, String name, boolean isDefault) { this.home = (home != null && !home.endsWith(File.separator) ? home + File.separator : home); this.name = name; this.isDefault = isDefault; } public File[] getBootstrapClasspath() { if (home == null || home.length() == 0) { return new File[0]; } Set<File> jars = new LinkedHashSet<File>(); File grailsHome = new File(home); if (grailsHome.exists()) { addBootstrapJar(jars, new File(home, "lib")); // addBootstrapJar(jars, new File(home, "lib/org.codehaus.groovy/groovy-all/jars")); addBootstrapJar(jars, new File(home, "dist")); } if (jars.isEmpty()) { //That's probably an issue. Rather than have this produce cryptic problems / errors much later. //Throw an error now that includes 'home' dir. //May help diagnose problems like: //http://forum.springsource.org/showthread.php?138848-Grails-2-2-2-problem-with-STS-2-9&p=449005#post449005 throw new Error("Couldn't find bootstrap classpath jars in Grails install at: '"+home+"'"); } return jars.toArray(new File[jars.size()]); } public File[] getDependencyClasspath() { if (home == null || home.length() > 0) { return new File[0]; } Set<File> jars = new LinkedHashSet<File>(); File grailsHome = new File(home); if (grailsHome.exists()) { for (File jar : new File(grailsHome + "/dist").listFiles()) { if (jar.isFile() && jar.getName().endsWith(".jar")) { jars.add(jar); } } for (File jar : new File(grailsHome + "/lib").listFiles()) { if (jar.isFile() && jar.getName().endsWith(".jar")) { jars.add(jar); } } String version = GrailsCoreActivator.getDefault() .getBundle().getHeaders().get(Constants.BUNDLE_VERSION); if (version.endsWith("qualifier")) { addBundleFile(jars, "/bin"); } else { addBundleFile(jars, "/"); } } return jars.toArray(new File[jars.size()]); } public String getHome() { return home; } public String getName() { return name; } public String getPluginHome(IProject project) { return getGrailsWorkDir() + File.separator + "projects" + File.separator + project.getName() + File.separator + "plugins"; } /** * Warning, don't use this method to determine the .grails work dir that grails * actually uses. This method only returns a "correct" result if either * - the grails work dir is set by calling setDefaultGrailsWorkDir => used during testing * - the grails workdir is the default grails computes itself (i.e. it was not set by the user in some * other way (e.g. by using settings.groovy to change it from the grails default)) * Instead, you can use the utility method in GrailsPluginUtil.getGrailsWorkDir. * * @return The location of the ".grails" folder (on the command line is set * by -Dgrails.work.dir=...) */ public String getGrailsWorkDir() { if (defaultGrailsWorkDir != null) return defaultGrailsWorkDir; else { String userHome = System.getProperty("user.home"); return userHome + File.separator + ".grails" + File.separator + getVersionString(); } } public String getVersionString() { if (home != null) { File buildProperties = new File(home, "build.properties"); if (buildProperties.exists()) { Properties props = new Properties(); try { props.load(new FileInputStream(buildProperties)); return props.getProperty("grails.version"); } catch (FileNotFoundException e) { } catch (IOException e) { } } } return UNKNOWN_VERSION_STRING; } public boolean isDefault() { return isDefault; } private void addBundleFile(Set<File> jars, String path) { try { URL embeddedUrl = FileLocator.toFileURL(GrailsCoreActivator .getDefault().getBundle().getEntry(path)); try { jars.add(new File(embeddedUrl.toURI())); } catch (Exception e) { jars.add(new File(embeddedUrl.getPath())); } } catch (IOException e1) { } } private void addBootstrapJar(Set<File> jars, File home) { if (home.isDirectory()) { File[] files = home.listFiles(); if (files!=null) { for (File file : files) { String name = file.getName(); if (file.isDirectory()) { addBootstrapJar(jars, file); } else if (name.endsWith(".jar") && ( name.startsWith("groovy-all") || name.startsWith("grails-bootstrap") ) && !( name.endsWith("sources.jar") || name.endsWith("javadoc.jar") )) { jars.add(file); } } } } } @Override public String toString() { return "DefaultGrailsInstall(" + home + ")"; } public GrailsVersion getVersion() { return new GrailsVersion(getVersionString()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((home == null) ? 0 : home.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DefaultGrailsInstall other = (DefaultGrailsInstall) obj; if (home == null) { if (other.home != null) return false; } else if (!home.equals(other.home)) return false; return true; } public IStatus verify() { String homeStr = getHome(); if (homeStr==null) { return error("Install '"+getName()+"' home is not set"); } File home = new File(homeStr); if (!home.exists()) { return error("Install '"+getName()+"' at location '"+homeStr+"' does not exist. Was it deleted or moved?"); } if (UNKNOWN_VERSION_STRING.equals(getVersionString()) || getVersionString()==null) { return error("Install '"+getName()+"' does not appear to be a valid Grails install."); } return Status.OK_STATUS; } private Status error(String msg) { return new Status(IStatus.ERROR, GrailsCoreActivator.PLUGIN_ID, msg); } private class SpringloadedJarFinder { /** * Maven artifact-ids plus trailing dash. These are the strings that we look for to * recognize a spring-loaded jar file name. */ private final String[] LOADED_ARTIFACT_IDS_WITH_DASH = { //Careful... one is a prefix of the other. Make sure the longer name is first. // The names are checked in the order given. Matching on the short name // inadvertently will cause a problem extracting the version string. "springloaded-core-", //prior to Grails 2.3.7 "springloaded-" //Grails 2.3.7 and up }; private final String[] searchIn = { "lib/com.springsource.springloaded/springloaded-core", "lib/org.springsource.springloaded/springloaded-core", "lib/org.springframework/springloaded" }; private File foundJar = null; private Version foundVersion = null; /** Get the springloaded jar, search for it the first time. Cached after that */ public File get() { //Only relevant for Grails 2.0.0 and up if (getVersion().compareTo(GrailsVersion.V_2_0_0)>=0) { if (foundJar==null) { for (String searchLoc : searchIn) { if (foundJar!=null) { break; } find(new File(getHome(), searchLoc)); } } } return foundJar; } private synchronized void find(File libPath) { File[] files = libPath.listFiles(); if (files!=null) { for (File jarCandidate : files) { if (jarCandidate.isFile()) { String fileName = jarCandidate.getName(); //String jarArtifactId = "springloaded-core-"; String jarArtifactId = determineAID(fileName); if (jarArtifactId!=null && fileName.startsWith(jarArtifactId) && fileName.endsWith(".jar") //ignore javadoc and source jars && fileName.indexOf("source")==-1 && fileName.indexOf("javadoc")==-1) { //Found a springloaded jar. String versionString = fileName.substring( jarArtifactId.length(), fileName.length()-4/*".jar".length()*/); Version version = new Version(versionString); //Not really an OSGi bundle, but this should work anyway. if (foundVersion==null || foundVersion.compareTo(version)<0) { //Only keep most recent version foundVersion = version; foundJar = jarCandidate; } } } else if (jarCandidate.isDirectory()) { //Extend search into sub directories (to support the mavenRepo-like file layout in Grails 2.0.2) find(jarCandidate); } } } } private String determineAID(String fileName) { for (String aid : LOADED_ARTIFACT_IDS_WITH_DASH) { if (fileName.startsWith(aid)) { return aid; } } return null; } } public File getSpringLoadedJar() { return loadedJarFinder.get(); } /** * Check whether a given javaInstall meets requirements to run commands for this Grails install. */ public void verifyJavaInstall(IVMInstall _javaInstall) throws CoreException { GrailsVersion grailsVersion = getVersion(); if (grailsVersion.compareTo(GrailsVersion.V_2_0_0)>=0) { //2.0 or above requires at least Java 1.6 if (_javaInstall instanceof IVMInstall2) { IVMInstall2 javaInstall = (IVMInstall2) _javaInstall; String javaVersion = javaInstall.getJavaVersion(); if (javaVersion!=null) { if (javaVersion.compareTo("1.6") < 0) { //String compare isn't strictly correct, but will work in this case (at least until we get to //Java version 1.10.x throw new CoreException(new Status(IStatus.ERROR, GrailsCoreActivator.PLUGIN_ID, "Grails "+grailsVersion+" requires at least Java 1.6.\n" + "The Java install at "+ _javaInstall.getInstallLocation()+"\n" + "is version "+javaVersion)); } } } } } public File getSpringLoadedCacheDir() { File wsMetadata = GrailsCoreActivator.getDefault().getStateLocation().toFile(); File cacheDir = new File(wsMetadata, getVersionString()); cacheDir.mkdirs(); return cacheDir; } }