/*******************************************************************************
* Copyright (c) 2013, 2017 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.core.cli.install;
import java.io.File;
import java.io.FilenameFilter;
import java.net.URI;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
public abstract class BootInstall implements IBootInstall {
private static final File[] NO_FILES = new File[0];
protected final String uriString;
/**
* Creates a BootInstall pointing to given url and a optional
* name. If the name is null or empty then a name will be
* generated automatically as needed.
*/
public BootInstall(String urlString, String name) {
Assert.isNotNull(urlString);
if (name!=null && !"".equals(name.trim())) {
this.name = name;
}
this.uriString = urlString;
}
static final FilenameFilter JAR_FILE_FILTER = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jar");
}
};
private static final String UNKNOWN_VERSION = "Unknown";
File[] bootLibJars; //Set once we determined the location of the spring-boot jar(s) for this install.
File[] extensionJars; // Extensions
private String name;
public abstract File getHome() throws Exception;
public File[] getBootLibJars() throws Exception {
//Example: .../installs/spring-boot-cli-0.5.0.M6-bin/spring-0.5.0.M6/lib/spring-boot-cli-0.5.0.M6.jar
if (bootLibJars==null) {
File home = getHome(); //Example: .../installs/spring-boot-cli-0.5.0.M6-bin/spring-0.5.0.M6/
//Expect to find spring-boot-cli-<version> in lib folder.
bootLibJars = new File(home, "lib").listFiles(JAR_FILE_FILTER);
if (bootLibJars==null) {
bootLibJars = NO_FILES;
}
}
return bootLibJars;
}
@Override
public File[] getExtensionsJars() throws Exception {
if (extensionJars == null && getHome() != null) {
File libFolder = new File(getHome(), "lib");
if (libFolder.exists()) {
File extFolder = new File(libFolder, "ext");
extensionJars = extFolder.exists() ? extFolder.listFiles(JAR_FILE_FILTER) : libFolder.listFiles();
} else {
extensionJars = NO_FILES;
}
}
return extensionJars;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((uriString == null) ? 0 : uriString.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;
BootInstall other = (BootInstall) obj;
if (uriString == null) {
if (other.uriString != null)
return false;
} else if (!uriString.equals(other.uriString))
return false;
return true;
}
@Override
public String getUrl() {
return uriString;
}
/**
* Create a name from the uri. Let the name be 'short' but reflect interesting bits such
* as the uri scheme (if not file) and the last segment of the path including extension.
* <p>
* That way it is possible to see if a distro is:
* remote <-> local
* zip <-> folder
*/
public String getName() {
if (name==null) {
name = defaultName();
}
return name;
}
/**
* Try to generate a good name for this boot install based on what is known about it
* (e.g. version, location etc).
*/
protected String defaultName() {
String version = getVersion();
if (version!=null && !version.equals(UNKNOWN_VERSION)) {
return "Boot "+version;
}
String lastSegment = lastSegment();
if (lastSegment!=null) {
return lastSegment;
}
return "Boot";
}
public String lastSegment() {
try {
URI uri = new URI(uriString);
String lastSegment = new Path(uri.getPath()).lastSegment();
return lastSegment;
} catch (Exception e) {
Log.log(e);
return null;
}
}
/**
* Try to determine the version of this installation based on what is known about it.
* Note that if a install is not downloaded yet we basically only have access to
* the Url String because it would be udesirable to download the entire zip just
* to check the version.
*/
@Override
public String getVersion() {
String lastSegment = lastSegment();
if (lastSegment!=null) {
//Example: spring-boot-cli-0.5.0.M6-bin.zip
if (lastSegment.toLowerCase().endsWith(".zip")) {
//Expect format: <artifact-id>-<version>-<classifier>.zip
int end = lastSegment.length()-4; //4 = '.zip'.length
end = lastSegment.lastIndexOf('-', end);
//end is now at start of -<classifier>
if (end>=0) {
int start = lastSegment.lastIndexOf('-', end-1);
if (start>=0) {
//start at the - before the version string
return lastSegment.substring(start+1, end);
}
}
} else {
//TODO: not a .zip assume its a folder.
}
}
return UNKNOWN_VERSION;
}
@Override
public IStatus validate() {
try {
if (mayRequireDownload()) {
//don't validate when it might trigger a download in the UI thread.
return Status.OK_STATUS;
} else {
File[] jars = getBootLibJars();
if (jars==null || jars.length==0) {
return new Status(IStatus.ERROR, BootActivator.PLUGIN_ID, "No boot install found at: "+getUrl());
} else {
//Anything that has lib folder with some jars will produce jars here....
// so check at leats on jar name is the expected spring-boot-cli jar.
for (File file : jars) {
if (file.getName().startsWith("spring-boot-cli")) {
return Status.OK_STATUS;
}
}
//found some lib jars so probably ok
return new Status(IStatus.ERROR, BootActivator.PLUGIN_ID, "spring-boot-cli jar not found in "+getUrl());
}
}
} catch (Exception e) {
Log.log(e);
return ExceptionUtil.status(e);
}
}
/**
* For content that may be remote this method
* should return true. False should only be returned in the case
* where the content is known to be local or already cached.
* <p>
* In the case of uncertainty the method should conservatively return true (assuming the worst case,
* scenario where a dowload will be required.
* <p>
* This method is used to determine whether it is safe to call methods that require
* the content of the install without triggering a lenghty download operation in the
* UI thread.
*/
protected boolean mayRequireDownload() {
String url = getUrl();
boolean isCertainlyLocal = url!=null && url.startsWith("file:");
return !isCertainlyLocal;
}
}