/******************************************************************************* * Copyright (c) 2013, 2016 Red Hat, 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: * Red Hat Inc. - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.thym.core.engine.internal.cordova; import java.io.File; import java.io.FileFilter; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FileUtils; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProduct; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.ecf.filetransfer.IncomingFileTransferException; import org.eclipse.ecf.filetransfer.identity.FileCreateException; import org.eclipse.ecf.filetransfer.identity.FileIDFactory; import org.eclipse.ecf.filetransfer.identity.IFileID; import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransfer; import org.eclipse.osgi.util.NLS; import org.eclipse.thym.core.HybridCore; import org.eclipse.thym.core.engine.AbstractEngineRepoProvider; import org.eclipse.thym.core.engine.HybridMobileEngine; import org.eclipse.thym.core.engine.HybridMobileEngineLocator; import org.eclipse.thym.core.engine.HybridMobileEngineLocator.EngineSearchListener; import org.eclipse.thym.core.engine.HybridMobileLibraryResolver; import org.eclipse.thym.core.extensions.CordovaEngineRepoProvider; import org.eclipse.thym.core.extensions.PlatformSupport; public class CordovaEngineProvider implements HybridMobileEngineLocator, EngineSearchListener { /** * Engine id for the engine provided by the Apache cordova project. */ public static final String CORDOVA_ENGINE_ID = "cordova"; public static final String CUSTOM_CORDOVA_ENGINE_ID = "custom_cordova"; private volatile static ArrayList<HybridMobileEngine> engineList; /** * List of engines that are locally available. This is the list of engines that * can be used by the projects. * * @return */ public List<HybridMobileEngine> getAvailableEngines() { initEngineList(); return engineList; } private void resetEngineList(){ engineList = null; } private void initEngineList() { if(engineList != null ) return; engineList = new ArrayList<HybridMobileEngine>(); File libFolder = getLibFolder().toFile(); if( !libFolder.isDirectory()){ //engine folder does not exist return; } //search for engines on default location. searchForRuntimes(new Path(libFolder.toString()), this, new NullProgressMonitor()); //Now the custom locations String[] locs = HybridCore.getDefault().getCustomLibraryLocations(); if(locs != null ){ for (int i = 0; i < locs.length; i++) { searchForRuntimes(new Path(locs[i]), this, new NullProgressMonitor()); } } } /** * User friendly name of the engine * * @return */ public String getName(){ return "Apache Cordova"; } public HybridMobileEngine getEngine(String id, String version){ initEngineList(); for (HybridMobileEngine engine : engineList) { if(engine.getVersion().equals(version) && engine.getId().equals(id)){ return engine; } } return null; } /** * Helper method for creating engines.. Clients should not * use this method but use {@link #getAvailableEngines()} or * {@link #getEngine(String)}. This method is left public * mainly to help with testing. * * @param version * @param platforms * @param resolver * @return */ public HybridMobileEngine createEngine(String id, String version, HybridMobileLibraryResolver resolver, IPath location){ HybridMobileEngine engine = new HybridMobileEngine(); engine.setId(id); engine.setName(NLS.bind("{0}-{1}", new String[]{CORDOVA_ENGINE_ID,id})); engine.setResolver(resolver); engine.setVersion(version); engine.setLocation(location); return engine; } public static IPath getLibFolder(){ IPath path = new Path(FileUtils.getUserDirectory().toString()); path = path.append(".cordova").append("lib"); return path; } public List<DownloadableCordovaEngine> getDownloadableVersions() throws CoreException { AbstractEngineRepoProvider provider = new NpmBasedEngineRepoProvider(); IProduct product = Platform.getProduct(); if (product != null) { String productId = Platform.getProduct().getId(); List<CordovaEngineRepoProvider> providerProxies = HybridCore .getCordovaEngineRepoProviders(); for (CordovaEngineRepoProvider providerProxy : providerProxies) { if (productId.equals(providerProxy.getProductId())) { provider = providerProxy.createProvider(); } } } return provider.getEngines(); } public void downloadEngine(DownloadableCordovaEngine[] engines, IProgressMonitor monitor) { if(monitor == null ){ monitor = new NullProgressMonitor(); } IRetrieveFileTransfer transfer = HybridCore.getDefault().getFileTransferService(); IFileID remoteFileID; int platformSize = engines.length; Object lock = new Object(); int incompleteCount = platformSize; SubMonitor sm = SubMonitor.convert(monitor,platformSize ); for (int i = 0; i < platformSize; i++) { sm.setTaskName("Download Cordova Engine "+engines[i].getVersion()); try { URI uri = URI.create(engines[i].getDownloadURL()); remoteFileID = FileIDFactory.getDefault().createFileID(transfer.getRetrieveNamespace(), uri); if(sm.isCanceled()){ return; } transfer.sendRetrieveRequest(remoteFileID, new EngineDownloadReceiver(engines[i].getVersion(), engines[i].getPlatformId(), lock, sm), null); } catch (FileCreateException e) { HybridCore.log(IStatus.ERROR, "Engine download file create error", e); } catch (IncomingFileTransferException e) { HybridCore.log(IStatus.ERROR, "Engine download file transfer error", e); } } synchronized (lock) { while(incompleteCount >0){ try { lock.wait(); } catch (InterruptedException e) { HybridCore.log(IStatus.INFO, "interrupted while waiting for all engines to download", e); } incompleteCount--; sm.worked(1); } } resetEngineList(); } /** * Check if the platform is supported by this provider. * * @param platformId * @return */ public boolean isSupportedPlatform(String version, String platformId){ Assert.isNotNull(platformId); Assert.isNotNull( version); List<DownloadableCordovaEngine> engines = null; try { engines = getDownloadableVersions(); } catch (CoreException e) { HybridCore.log(IStatus.ERROR, "Error retrieving downloadable engines", e); } if(engines == null ){ return false; } for (DownloadableCordovaEngine downloadable : engines) { if(downloadable.getVersion().equals(version) && downloadable.getPlatformId().equals(platformId) ){ return true; } } return false; } @Override public void searchForRuntimes(IPath path, EngineSearchListener listener, IProgressMonitor monitor) { if( path == null ) return; File root = path.toFile(); if(!root.isDirectory()) return; searchDir(root, listener, monitor); } private void searchDir(File dir, EngineSearchListener listener, IProgressMonitor monitor){ if("bin".equals(dir.getName())){ File createScript = new File(dir,"create"); if(createScript.exists()){ Path libraryRoot = new Path(dir.getParent()); List<PlatformSupport> platforms = HybridCore.getPlatformSupports(); for (PlatformSupport platformSupport : platforms) { try { HybridMobileLibraryResolver resolver = platformSupport.getLibraryResolver(); resolver.init(libraryRoot); if(resolver.isLibraryConsistent().isOK()){ HybridMobileEngine engine = createEngine(platformSupport.getPlatformId(),resolver.detectVersion(), resolver,libraryRoot); listener.engineFound(engine); return; } } catch (CoreException e) { HybridCore.log(IStatus.WARNING, "Error on engine search", e); } } } } //search the sub-directories File[] dirs = dir.listFiles(new FileFilter() { @Override public boolean accept(File f) { return f.isDirectory(); } }); if(dirs != null ){ for (int i = 0; i < dirs.length; i++) { if(!monitor.isCanceled()){ String name = dirs[i].getName(); if(name.equals("npm_cache") || name.equals("tmp")){ continue; } searchDir(dirs[i], listener, monitor); } } } } @Override public void engineFound(HybridMobileEngine engine) { engineList.add(engine); } public void deleteEngineLibraries(HybridMobileEngine selectedEngine) { IPath path = selectedEngine.getLocation(); FileUtils.deleteQuietly(path.toFile()); resetEngineList(); } }