/******************************************************************************* * Copyright (c) 2009 Vlad Dumitrescu and others. 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: Vlad Dumitrescu *******************************************************************************/ package org.erlide.backend.internal; import java.io.File; import java.io.IOException; import java.util.Collection; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.IStreamListener; import org.eclipse.debug.core.model.IStreamMonitor; import org.eclipse.debug.core.model.IStreamsProxy; import org.eclipse.jdt.annotation.NonNull; import org.erlide.backend.BackendCore; import org.erlide.backend.BackendUtils; import org.erlide.backend.api.BackendData; import org.erlide.backend.api.IBackend; import org.erlide.backend.api.IBackendManager; import org.erlide.backend.api.ICodeBundle; import org.erlide.backend.api.ICodeBundle.CodeContext; import org.erlide.backend.console.BackendShellManager; import org.erlide.backend.debug.BeamUtil; import org.erlide.backend.debug.ErlideDebug; import org.erlide.backend.debug.model.ErlangDebugTarget; import org.erlide.engine.model.root.IErlProject; import org.erlide.runtime.api.BeamLoader; import org.erlide.runtime.api.IOtpNodeProxy; import org.erlide.runtime.rpc.IOtpRpc; import org.erlide.runtime.shell.IBackendShell; import org.erlide.runtime.shell.IoRequest.IoRequestKind; import org.erlide.util.ErlLogger; import org.erlide.util.ErlangFunctionCall; import org.erlide.util.SystemConfiguration; import com.ericsson.otp.erlang.OtpErlangBinary; import com.ericsson.otp.erlang.OtpErlangPid; import com.google.common.collect.Lists; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Service; import com.google.common.util.concurrent.Service.Listener; import com.google.common.util.concurrent.Service.State; public class Backend implements IStreamListener, IBackend { private final IOtpNodeProxy runtime; private BackendShellManager shellManager; private CodeManager codeManager; private final BackendData data; private ErlangDebugTarget debugTarget; protected final IBackendManager backendManager; private boolean disposed = false; public Backend(final BackendData data, @NonNull final IOtpNodeProxy runtime, final IBackendManager backendManager) { this.runtime = runtime; this.data = data; this.backendManager = backendManager; } @Override public synchronized void dispose() { if (disposed) { return; } disposed = true; if (data.isDebug() && debugTarget != null) { debugTarget.dispose(); } if (shellManager != null) { shellManager.dispose(); shellManager = null; } runtime.dispose(); BackendCore.getBackendManager().removeBackend(this); } @Override public String getName() { return runtime.getNodeName(); } protected boolean startErlideApps(final OtpErlangPid jRex, final boolean watch) { try { final IOtpRpc site = getOtpRpc(); final SystemConfiguration sysconf = SystemConfiguration.getInstance(); site.call("erlide_common_app", "init", "poiii", jRex, watch, sysconf.getWarnProcessSizeLimitMB(), sysconf.getKillProcessSizeLimitMB(), sysconf.getMaxParallelBuilds()); // TODO should use extension point! switch (data.getContext()) { case BUILDER: site.call("erlide_builder_app", "init", ""); break; case IDE: site.call("erlide_builder_app", "init", ""); site.call("erlide_ide_app", "init", ""); break; default: } // site.call("erlide_tracer", "start", ""); return true; } catch (final Exception e) { ErlLogger.error(e); return false; } } @Override public boolean isRunning() { return runtime.isRunning(); } public void removePath(final @NonNull String path) { codeManager.removePath(path); } public void addPath(final boolean usePathZ, final @NonNull String path) { codeManager.addPath(usePathZ, path); } public synchronized void initErlang(final boolean watch) { startErlideApps(getRuntime().getEventPid(), watch); } @Override public void registerCodeBundle(final CodeContext context, final ICodeBundle bundle) { codeManager.register(context, bundle); } @Override public void unregisterCodeBundle(final CodeContext context, final ICodeBundle b) { codeManager.unregister(context, b); } @Override public void streamAppended(final String text, final IStreamMonitor monitor) { final IStreamsProxy proxy = getStreamsProxy(); if (monitor == proxy.getOutputStreamMonitor()) { // System.out.println(getName() + " OUT " + text); } else if (monitor == proxy.getErrorStreamMonitor()) { // System.out.println(getName() + " ERR " + text); } else { // System.out.println("???" + text); } } public void assignStreamProxyListeners() { if (data.getLaunch() == null) { return; } final IStreamsProxy proxy = getStreamsProxy(); if (proxy != null) { final IStreamMonitor errorStreamMonitor = proxy.getErrorStreamMonitor(); errorStreamMonitor.addListener(this); final IStreamMonitor outputStreamMonitor = proxy.getOutputStreamMonitor(); outputStreamMonitor.addListener(this); } } @Override public IBackendShell getShell(final String id) { final IBackendShell shell = shellManager.openShell(id); final IStreamsProxy proxy = getStreamsProxy(); if (proxy != null) { final IStreamMonitor errorStreamMonitor = proxy.getErrorStreamMonitor(); errorStreamMonitor.addListener(new IStreamListener() { @Override public void streamAppended(final String text, final IStreamMonitor monitor) { shell.add(text, IoRequestKind.STDERR); } }); final IStreamMonitor outputStreamMonitor = proxy.getOutputStreamMonitor(); outputStreamMonitor.addListener(new IStreamListener() { @Override public void streamAppended(final String text, final IStreamMonitor monitor) { shell.add(text, IoRequestKind.STDOUT); } }); } return shell; } @Override public void input(final String s) throws IOException { if (isRunning()) { final IStreamsProxy proxy = getStreamsProxy(); if (proxy != null) { proxy.write(s); } else { ErlLogger.warn("Could not send input to backend %s, stream proxy is null", getName()); } } } @Override public void addProjectPath(final IErlProject eproject) { if (eproject == null) { return; } final IProject project = eproject.getWorkspaceProject(); final String outDir = project.getLocation() .append(eproject.getProperties().getOutputDir()).toOSString(); if (outDir.length() > 0) { final boolean accessible = BackendUtils.isAccessibleDir(getOtpRpc(), outDir); if (accessible) { addPath(false/* prefs.getUsePathZ() */, outDir); } else { loadBeamsFromDir(outDir); } } } @Override public void removeProjectPath(final IErlProject eproject) { if (eproject == null) { // can happen if project was removed return; } try { final IProject project = eproject.getWorkspaceProject(); final String outDir = project.getLocation() .append(eproject.getProperties().getOutputDir()).toOSString(); if (outDir.length() > 0) { removePath(outDir); // TODO unloadBeamsFromDir(outDir); ? } } catch (final Exception e) { // can happen when shutting down, ignore } } protected IStreamsProxy getStreamsProxy() { return null; } protected void postLaunch() throws DebugException { final Collection<IProject> projects = Lists.newArrayList(data.getProjects()); registerProjectsWithExecutionBackend(projects); if (data.isDebug()) { // add debugTarget final ILaunch launch = getData().getLaunch(); if (!debuggerIsRunning()) { debugTarget = new ErlangDebugTarget(launch, this, projects); launch.addDebugTarget(debugTarget); registerStartupFunctionStarter(data); debugTarget.sendStarted(); } } else if (data.isManaged()) { // don't run this if the node is already running final ErlangFunctionCall initCall = data.getInitialCall(); if (initCall != null) { runInitial(initCall.getModule(), initCall.getName(), initCall.getParameters()); } } } private boolean debuggerIsRunning() { return ErlideDebug.isRunning(getOtpRpc()); } private void registerProjectsWithExecutionBackend( final Collection<IProject> projects) { for (final IProject project : projects) { backendManager.addExecutionBackend(project, this); } } private void registerStartupFunctionStarter(final BackendData myData) { DebugPlugin.getDefault().addDebugEventListener(new IDebugEventSetListener() { @Override public void handleDebugEvents(final DebugEvent[] events) { final ErlangFunctionCall initCall = myData.getInitialCall(); if (initCall != null) { runInitial(initCall.getModule(), initCall.getName(), initCall.getParameters()); } DebugPlugin.getDefault().removeDebugEventListener(this); } }); } void runInitial(final String module, final String function, final String args) { try { if (module.length() > 0 && function.length() > 0) { ErlLogger.debug("calling startup function %s:%s", module, function); if (args.length() > 0) { getOtpRpc().cast(module, function, "s", args); } else { getOtpRpc().cast(module, function, ""); } } } catch (final Exception e) { ErlLogger.debug("Could not run initial call %s:%s(\"%s\")", module, function, args); ErlLogger.warn(e); } } @Override public BackendData getData() { return data; } @Override public void initialize(final CodeContext context, final Collection<ICodeBundle> bundles) { shellManager = new BackendShellManager(this); runtime.addRuntimeListener(new Listener() { @Override public void terminated(final Service.State from) { dispose(); getData().setLaunch(null); } @Override public void failed(final State from, final Throwable failure) { dispose(); getData().setLaunch(null); } @Override public void running() { codeManager = new CodeManager(getOtpRpc(), data.getRuntimeInfo().getName(), data.getRuntimeInfo().getVersion()); for (final ICodeBundle bb : bundles) { registerCodeBundle(context, bb); } initErlang(data.isManaged()); try { postLaunch(); } catch (final DebugException e) { ErlLogger.error(e); } } }, MoreExecutors.sameThreadExecutor()); } // ///// @Override public IOtpRpc getOtpRpc() { return runtime.getOtpRpc(); } @Override public IOtpNodeProxy getRuntime() { return runtime; } private void loadBeamsFromDir(final String outDir) { final File dir = new File(outDir); if (dir.isDirectory()) { for (final File f : dir.listFiles()) { final Path path = new Path(f.getPath()); if (path.getFileExtension() != null && "beam".compareTo(path.getFileExtension()) == 0) { final String m = path.removeFileExtension().lastSegment(); try { boolean ok = false; final OtpErlangBinary bin = BeamUtil.getBeamBinary(m, path); if (bin != null) { ok = BeamLoader.loadBeam(getOtpRpc(), m, bin); } if (!ok) { ErlLogger.error("Could not load %s", m); } } catch (final Exception ex) { ErlLogger.warn(ex); } } } } } @Override public boolean isDebugging() { try { return "debug".equals(getData().getLaunch().getLaunchMode()); } catch (final Exception e) { return false; } } }