/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.maven.server; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; import org.eclipse.che.maven.data.MavenExplicitProfiles; import org.eclipse.che.maven.data.MavenModel; import org.eclipse.che.maven.server.MavenRemoteServer; import org.eclipse.che.maven.server.MavenServer; import org.eclipse.che.maven.server.MavenServerDownloadListener; import org.eclipse.che.maven.server.MavenServerLogger; import org.eclipse.che.maven.server.MavenSettings; import org.eclipse.che.maven.server.MavenTerminal; import org.eclipse.che.maven.server.ProfileApplicationResult; import org.eclipse.che.plugin.maven.server.execution.CommandLine; import org.eclipse.che.plugin.maven.server.execution.JavaParameters; import org.eclipse.che.plugin.maven.server.execution.ProcessExecutor; import org.eclipse.che.plugin.maven.server.execution.ProcessHandler; import org.eclipse.che.plugin.maven.server.rmi.RmiClient; import org.eclipse.che.plugin.maven.server.rmi.RmiObjectWrapper; import org.eclipse.che.rmi.RmiObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PreDestroy; import java.io.File; import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * @author Evgen Vidolob */ @Singleton public class MavenServerManager extends RmiObjectWrapper<MavenRemoteServer> { private static final Logger LOG = LoggerFactory.getLogger(MavenServerManager.class); private static final String MAVEN_SERVER_MAIN = "org.eclipse.che.maven.server.MavenServerMain"; private RmiClient<MavenRemoteServer> client; private RmiLogger rmiLogger = new RmiLogger(); private RmiMavenServerDownloadListener rmiDownloadListener = new RmiMavenServerDownloadListener(); private boolean loggerExported; private boolean listenerExported; private String mavenServerPath; private File localRepository; @Inject public MavenServerManager(@Named("che.maven.server.path") String mavenServerPath) { this.mavenServerPath = mavenServerPath; client = new RmiClient<MavenRemoteServer>(MavenRemoteServer.class) { @Override protected ProcessExecutor getExecutor() { return createExecutor(); } }; } private static void addDirToClasspath(List<String> classPath, File dir) { File[] jars = dir.listFiles((dir1, name) -> { return name.endsWith(".jar"); }); if (jars == null) { return; } for (File jar : jars) { classPath.add(jar.getAbsolutePath()); } } private ProcessExecutor createExecutor() { return () -> { JavaParameters parameters = buildMavenServerParameters(); CommandLine command = parameters.createCommand(); return new ProcessHandler(command.createProcess()); }; } public MavenServerWrapper createMavenServer() { return new MavenServerWrapper() { @Override protected MavenServer create() throws RemoteException { MavenSettings mavenSettings = new MavenSettings(); //TODO add more user settings mavenSettings.setMavenHome(new File(System.getenv("M2_HOME"))); mavenSettings.setUserSettings(new File(System.getProperty("user.home"), ".m2/settings.xml")); // Setting Global maven setting // for more maven info settings visit https://maven.apache.org/settings.html mavenSettings.setGlobalSettings(new File(System.getenv("M2_HOME"), "conf/settings.xml")); mavenSettings.setLoggingLevel(MavenTerminal.LEVEL_INFO); if (localRepository != null) { mavenSettings.setLocalRepository(localRepository); } return MavenServerManager.this.getOrCreateWrappedObject().createServer(mavenSettings); } }; } /** * For test use only. Sets the path to local maven repository * * @param localRepository */ public void setLocalRepository(File localRepository) { this.localRepository = localRepository; } public MavenModel interpolateModel(MavenModel model, File projectDir) { return perform(() -> getOrCreateWrappedObject().interpolateModel(model, projectDir)); } public ProfileApplicationResult applyProfiles(MavenModel model, File projectDir, MavenExplicitProfiles explicitProfiles, Collection<String> alwaysOnProfiles) { return perform(() -> getOrCreateWrappedObject().applyProfiles(model, projectDir, explicitProfiles, alwaysOnProfiles)); } @PreDestroy public void shutdown() { client.stopAll(false); cleanUp(); } @Override protected MavenRemoteServer create() throws RemoteException { MavenRemoteServer server; try { server = client.acquire(this, ""); } catch (Exception e) { throw new RemoteException("Can't start maven server", e); } if (!loggerExported) { Remote loggerRemote = UnicastRemoteObject.exportObject(rmiLogger, 0); if (!(loggerExported = loggerRemote != null)) { throw new RemoteException("Can't export logger"); } } if (!listenerExported) { Remote listenerRemote = UnicastRemoteObject.exportObject(rmiDownloadListener, 0); if (!(listenerExported = listenerRemote != null)) { throw new RemoteException("Can't export download listener"); } } server.configure(rmiLogger, rmiDownloadListener); return server; } @Override protected synchronized void cleanUp() { super.cleanUp(); if (loggerExported) { try { UnicastRemoteObject.unexportObject(rmiLogger, true); } catch (NoSuchObjectException e) { LOG.error("Can't unexport RMI logger", e); } loggerExported = false; } if (listenerExported) { try { UnicastRemoteObject.unexportObject(rmiDownloadListener, true); } catch (NoSuchObjectException e) { LOG.error("Can't unexport RMI artifact download listener", e); } listenerExported = false; } } public JavaParameters buildMavenServerParameters() { JavaParameters parameters = new JavaParameters(); parameters.setJavaExecutable("java"); parameters.setWorkingDirectory(System.getProperty("java.io.tmpdir")); parameters.setMainClassName(MAVEN_SERVER_MAIN); //TODO read and set MAVEN_OPTS system properties List<String> classPath = new ArrayList<>(); addDirToClasspath(classPath, new File(mavenServerPath)); String mavenHome = System.getenv("M2_HOME"); addDirToClasspath(classPath, new File(mavenHome, "lib")); File bootDir = new File(mavenHome, "boot"); File[] classworlds = bootDir.listFiles((dir, name) -> { return name.contains("classworlds"); }); if (classworlds != null) { for (File file : classworlds) { classPath.add(file.getAbsolutePath()); } } parameters.getClassPath().addAll(classPath); parameters.getVmParameters().add("-Xmx512m"); return parameters; } private <T> T perform(RunnableRemoteWithResult<T> runnable) { RemoteException exception = null; for (int i = 0; i < 2; i++) { try { return runnable.perform(); } catch (RemoteException e) { exception = e; onError(); } } throw new RuntimeException(exception); } private interface RunnableRemoteWithResult<T> { T perform() throws RemoteException; } private class RmiLogger extends RmiObject implements MavenServerLogger { @Override public void info(Throwable t) throws RemoteException { LOG.info(t.getMessage(), t); } @Override public void warning(Throwable t) throws RemoteException { LOG.warn(t.getMessage(), t); } @Override public void error(Throwable t) throws RemoteException { LOG.error(t.getMessage(), t); } } private class RmiMavenServerDownloadListener extends RmiObject implements MavenServerDownloadListener { @Override public void artifactDownloaded(File file, String relativePath) throws RemoteException { System.out.println("On download - " + relativePath); //todo notify browser about that } } }