/******************************************************************************* * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * *******************************************************************************/ package com.liferay.ide.project.core.modules; import aQute.bnd.deployer.repository.FixedIndexedRepo; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.Processor; import com.liferay.ide.core.LiferayCore; import com.liferay.ide.core.StringBufferOutputStream; import com.liferay.ide.core.util.PropertiesUtil; import com.liferay.ide.project.core.ProjectCore; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.net.URL; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Scanner; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Java; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.osgi.framework.Bundle; import org.osgi.framework.Version; /** * @author Gregory Amerson * @author Andy Wu */ public class BladeCLI { static final File _settingsDir = LiferayCore.GLOBAL_SETTINGS_PATH.toFile(); static final String localJarKey = "localjar"; static final File repoCache = new File( _settingsDir, "repoCache" ); static final String defaultRepoUrl = "http://releases.liferay.com/tools/blade-cli/2.x/"; static final String timeStampKey = "up2date.check"; public static final String BLADE_CLI_REPO_URL = "BLADE_CLI_REPO_URL"; public static final String BLADE_CLI_REPO_UP2DATE_CHECK = "BLADE_CLI_REPO_UP2DATE_CHECK"; static IPath cachedBladeCLIPath; public static String[] execute( String args ) throws BladeCLIException { final IPath bladeCLIPath = getBladeCLIPath(); if( bladeCLIPath == null || !bladeCLIPath.toFile().exists() ) { throw new BladeCLIException("Could not get blade cli jar."); } final Project project = new Project(); final Java javaTask = new Java(); javaTask.setProject( project ); javaTask.setFork( true ); javaTask.setFailonerror( true ); javaTask.setJar( bladeCLIPath.toFile() ); javaTask.setArgs( args ); final DefaultLogger logger = new DefaultLogger(); project.addBuildListener(logger); final StringBufferOutputStream out = new StringBufferOutputStream(); logger.setOutputPrintStream( new PrintStream( out ) ); logger.setMessageOutputLevel(Project.MSG_INFO); int returnCode = javaTask.executeJava(); final List<String> lines = new ArrayList<>(); final Scanner scanner = new Scanner( out.toString() ); while( scanner.hasNextLine() ) { lines.add( scanner.nextLine().replaceAll( ".*\\[null\\] ", "" ) ); } scanner.close(); boolean hasErrors = false; final StringBuilder errors = new StringBuilder(); for( String line : lines ) { if( line.startsWith( "Error" ) ) { hasErrors = true; } else if( hasErrors ) { errors.append( line ); } } if( returnCode != 0 || hasErrors ) { throw new BladeCLIException( errors.toString() ); } return lines.toArray( new String[0] ); } public static synchronized IPath getBladeCLIPath() throws BladeCLIException { final IPath stateLocation = ProjectCore.getDefault().getStateLocation(); Bundle bundle = Platform.getBundle( ProjectCore.PLUGIN_ID ); File stateDir = stateLocation.toFile(); String filePrefix = "blade-cache"; String currentVersionFileName = filePrefix + "-" + bundle.getVersion().toString() + ".properties"; final File bladeCacheSettingsFile = new File( stateDir, currentVersionFileName ); // clean old version blade-cache files if( stateDir.exists() ) { File[] children = stateDir.listFiles(); if( children.length > 0 ) { for( File child : children ) { if( child.isFile() && child.getName().startsWith( filePrefix ) && !child.getName().equals( currentVersionFileName ) ) { child.delete(); } } } } final SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMddHHmmss" ); if( shouldUpdate( sdf, bladeCacheSettingsFile) ) { try { updateLocalJar( sdf, bladeCacheSettingsFile ); } catch( Exception e ) { throw new BladeCLIException( "Unable to update local blade cli", e ); } } return cachedBladeCLIPath; } private static String getLatestRemoteBladeCLIJar() { _settingsDir.mkdirs(); repoCache.mkdirs(); Processor reporter = new Processor(); FixedIndexedRepo repo = new FixedIndexedRepo(); Map<String, String> props = new HashMap<String, String>(); props.put( "name", "index1" ); props.put( "locations", getRepoURL() + "index.xml.gz" ); props.put( FixedIndexedRepo.PROP_CACHE, repoCache.getAbsolutePath() ); repo.setProperties( props ); repo.setReporter( reporter ); try { File[] files = repo.get( "com.liferay.blade.cli", "[2,3)" ); File cliJar = files[0]; try( Jar cliJarJar = new Jar( cliJar ); Jar localJar = new Jar( getLocalCopy() ) ) { Version cliJarVersion = new Version( cliJarJar.getVersion() ); Version localCopyVersion = new Version( localJar.getVersion() ); if( cliJarVersion.compareTo( localCopyVersion ) >= 0 ) { cachedBladeCLIPath = new Path( cliJar.getCanonicalPath() ); } else { return null; } } return cliJar.getName(); } catch( Exception e ) { return null; } } public static synchronized String[] getProjectTemplates() throws BladeCLIException { List<String> templateNames = new ArrayList<>(); String[] retval = execute( "create -l" ); Collections.addAll( templateNames, retval ); return templateNames.toArray( new String[0] ); } private static File getRepoCacheDir() throws Exception { String repoURL = getRepoURL(); String retVal = URLEncoder.encode( repoURL, "UTF-8" ); return new File( repoCache, retVal + "plugins" ); } private static String getRepoURL() { IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode( ProjectCore.PLUGIN_ID ); String repoURL = prefs.get( BLADE_CLI_REPO_URL, defaultRepoUrl ); if( !repoURL.endsWith( "/" ) ) { repoURL = repoURL + "/"; } return repoURL; } private static String getValidTime() { IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode( ProjectCore.PLUGIN_ID ); return prefs.get( BLADE_CLI_REPO_UP2DATE_CHECK, "24h" ); } private static boolean shouldUpdate( SimpleDateFormat sdf, File bladeCacheSettingsFile ) throws BladeCLIException { boolean shouldUpdate = false; // file doesn't exist if( !bladeCacheSettingsFile.exists() ) { return true; } Properties props = PropertiesUtil.loadProperties( bladeCacheSettingsFile ); // can't load properties if( props == null ) { return true; } String up2dateCheckTimestamp = props.getProperty( timeStampKey ); Date lastTime = null; try { lastTime = sdf.parse( up2dateCheckTimestamp ); } catch( ParseException e ) { } // can't parse time if( lastTime == null ) { return true; } String validTime = getValidTime(); if( validTime.equals( "-1" ) ) { // do nothing } else if( validTime.equals( "0" ) ) { shouldUpdate = true; } else { String scope; int count; try { scope = validTime.substring( validTime.length() - 1, validTime.length() ); String countStr = validTime.substring( 0, validTime.length() - 1 ); count = Integer.parseInt( countStr ); } catch( Exception e ) { scope = "h"; count = 24; } Date currentTime = new Date(); long distance = currentTime.getTime() - lastTime.getTime(); if( scope.equals( "h" ) ) { long hours = distance / 1000 / 3600; if( hours > count ) { shouldUpdate = true; } } else if( scope.equals( "m" ) ) { long minutes = distance / 1000 / 60; if( minutes > count ) { shouldUpdate = true; } } else if( scope.equals( "s" ) ) { long seconds = distance / 1000; if( seconds > count ) { shouldUpdate = true; } } else { shouldUpdate = true; } } if( !shouldUpdate ) { try { File localJarFile = new File( getRepoCacheDir(), props.getProperty( localJarKey ) ); if( !localJarFile.exists() ) { return true; } else { cachedBladeCLIPath = new Path( localJarFile.getCanonicalPath() ); } } catch( Exception e ) { throw new BladeCLIException( e.getMessage() ); } } return shouldUpdate; } private static void updateLocalJar(SimpleDateFormat sdf, File bladeCacheSettings ) throws Exception { String latestJar = getLatestRemoteBladeCLIJar(); if( latestJar == null ) { File bundledJar = getLocalCopy(); latestJar = bundledJar.getName(); cachedBladeCLIPath = new Path( bundledJar.getCanonicalPath() ); } Properties props = new Properties(); props.setProperty( timeStampKey, sdf.format( new Date() ) ); props.setProperty( localJarKey, latestJar ); PropertiesUtil.saveProperties( props, bladeCacheSettings ); } private static File getLocalCopy() throws IOException { // use plugin copy URL url = FileLocator.toFileURL( ProjectCore.getDefault().getBundle().getEntry( "lib/com.liferay.blade.cli.jar" ) ); return new File( url.getFile() ); } }