| /******************************************************************************* |
| * Copyright (c) 2003, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ant.internal.launching.launchConfigurations; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.net.SocketException; |
| import java.net.SocketTimeoutException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import org.apache.tools.ant.Project; |
| import org.eclipse.ant.internal.core.AbstractEclipseBuildLogger; |
| import org.eclipse.ant.internal.launching.AntLaunch; |
| import org.eclipse.ant.internal.launching.AntLaunching; |
| import org.eclipse.ant.internal.launching.AntLaunchingUtil; |
| import org.eclipse.ant.internal.launching.IAntLaunchingPreferenceConstants; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchesListener; |
| import org.eclipse.debug.core.model.IProcess; |
| |
| /** |
| * Parts adapted from org.eclipse.jdt.internal.junit.ui.RemoteTestRunnerClient The client side of the RemoteAntBuildLogger. Handles the marshaling of |
| * the different messages. |
| */ |
| public class RemoteAntBuildListener implements ILaunchesListener { |
| public abstract class ListenerSafeRunnable implements ISafeRunnable { |
| @Override |
| public void handleException(Throwable exception) { |
| AntLaunching.log(exception); |
| } |
| } |
| |
| /** |
| * The server socket |
| */ |
| private ServerSocket fServerSocket; |
| private Socket fSocket; |
| private BufferedReader fBufferedReader; |
| private IProcess fProcess; |
| private String fProcessId; |
| private List<String> fMessageQueue; |
| protected ILaunch fLaunch; |
| private String fLastFileName = null; |
| private String fLastTaskName = null; |
| private boolean fBuildFailed = false; |
| /** |
| * The encoding to use |
| * |
| * @since 3.7 |
| */ |
| private String fEncoding; |
| |
| /** |
| * Reads the message stream from the RemoteAntBuildLogger |
| */ |
| private class ServerConnection extends Thread { |
| private int fServerPort; |
| |
| public ServerConnection(int port) { |
| super("Ant Build Server Connection"); //$NON-NLS-1$ |
| setDaemon(true); |
| fServerPort = port; |
| } |
| |
| @Override |
| public void run() { |
| try { |
| fServerSocket = new ServerSocket(fServerPort); |
| int socketTimeout = Platform.getPreferencesService().getInt(AntLaunching.getUniqueIdentifier(), IAntLaunchingPreferenceConstants.ANT_COMMUNICATION_TIMEOUT, 20000, null); |
| fServerSocket.setSoTimeout(socketTimeout); |
| fSocket = fServerSocket.accept(); |
| fBufferedReader = new BufferedReader(new InputStreamReader(fSocket.getInputStream(), fEncoding)); |
| // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=414516 |
| // the launch can be terminated but we haven't been notified yet |
| String message; |
| while (fLaunch != null && !fLaunch.isTerminated() && fBufferedReader != null && (message = fBufferedReader.readLine()) != null) { |
| receiveMessage(message); |
| } |
| } |
| catch (SocketException e) { |
| AntLaunching.log(e); |
| } |
| catch (SocketTimeoutException e) { |
| AntLaunching.log(e); |
| } |
| catch (IOException e) { |
| AntLaunching.log(e); |
| } |
| shutDown(); |
| } |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param launch |
| * the backing launch to listen to |
| * @param encoding |
| * the encoding to use for communications |
| */ |
| public RemoteAntBuildListener(ILaunch launch, String encoding) { |
| super(); |
| fLaunch = launch; |
| fEncoding = encoding; |
| DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); |
| } |
| |
| /** |
| * Returns the encoding set on the listener |
| * |
| * @return the encoding set on the listener |
| * @since 3.7 |
| */ |
| protected String getEncoding() { |
| return fEncoding; |
| } |
| |
| /** |
| * Start listening to an Ant build. Start a server connection that the RemoteAntBuildLogger can connect to. |
| * |
| * @param eventPort |
| * The port number to create the server connection on |
| */ |
| public synchronized void startListening(int eventPort) { |
| ServerConnection connection = new ServerConnection(eventPort); |
| connection.start(); |
| } |
| |
| protected synchronized void shutDown() { |
| fLaunch = null; |
| if (DebugPlugin.getDefault() != null) { |
| DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); |
| } |
| try { |
| if (fBufferedReader != null) { |
| fBufferedReader.close(); |
| fBufferedReader = null; |
| } |
| } |
| catch (IOException e) { |
| AntLaunching.log(e); |
| } |
| try { |
| if (fSocket != null) { |
| fSocket.close(); |
| fSocket = null; |
| } |
| } |
| catch (IOException e) { |
| AntLaunching.log(e); |
| } |
| try { |
| if (fServerSocket != null) { |
| fServerSocket.close(); |
| fServerSocket = null; |
| } |
| } |
| catch (IOException e) { |
| AntLaunching.log(e); |
| } |
| } |
| |
| protected void receiveMessage(String message) { |
| if (message.startsWith(MessageIds.TASK)) { |
| receiveTaskMessage(message); |
| } else if (message.startsWith(MessageIds.TARGET)) { |
| receiveTargetMessage(message); |
| } else if (message.startsWith(MessageIds.PROCESS_ID)) { |
| fProcessId = message.substring(MessageIds.PROCESS_ID.length()); |
| } else { |
| int index = message.indexOf(','); |
| if (index > 0) { |
| int priority = Integer.parseInt(message.substring(0, index)); |
| String msg = message.substring(index + 1); |
| writeMessage(msg + System.getProperty("line.separator"), priority); //$NON-NLS-1$ |
| if (msg.startsWith("BUILD FAILED")) { //$NON-NLS-1$ |
| fBuildFailed = true; |
| } else if (fBuildFailed) { |
| if (msg.startsWith("Total time:")) { //$NON-NLS-1$ |
| fBuildFailed = false; |
| } else { |
| AntLaunchingUtil.linkBuildFailedMessage(msg, getProcess()); |
| } |
| } |
| |
| } |
| } |
| } |
| |
| private void receiveTargetMessage(String message) { |
| String msg = message.substring(MessageIds.TARGET.length()); |
| StringTokenizer tokenizer = new StringTokenizer(msg, ","); //$NON-NLS-1$ |
| msg = tokenizer.nextToken(); |
| if (tokenizer.hasMoreTokens()) { |
| int locationLength = Integer.parseInt(tokenizer.nextToken()); |
| String location = tokenizer.nextToken(); |
| while (location.length() < locationLength) { // path with a comma in |
| // it |
| location += ","; //$NON-NLS-1$ |
| location += tokenizer.nextToken(); |
| } |
| int lineNumber = Integer.parseInt(tokenizer.nextToken()); |
| generateLink(msg, location, lineNumber, 0, msg.length() - 1); |
| } |
| writeMessage(msg + System.getProperty("line.separator"), Project.MSG_INFO); //$NON-NLS-1$ |
| } |
| |
| private void receiveTaskMessage(String message) { |
| String msg = message.substring(MessageIds.TASK.length()); |
| int index = msg.indexOf(','); |
| int priority = Integer.parseInt(msg.substring(0, index)); |
| int index2 = msg.indexOf(',', index + 1); |
| String taskName = msg.substring(index + 1, index2); |
| if (taskName.length() == 0) { |
| taskName = fLastTaskName; |
| } |
| int index3 = msg.indexOf(',', index2 + 1); |
| int lineLength = Integer.parseInt(msg.substring(index2 + 1, index3)); |
| int index4 = index3 + 1 + lineLength; |
| String line = msg.substring(index3 + 1, index4); |
| StringBuilder labelBuff = new StringBuilder(); |
| labelBuff.append('['); |
| labelBuff.append(taskName); |
| labelBuff.append("] "); //$NON-NLS-1$ |
| labelBuff.append(line); |
| line = labelBuff.toString(); |
| |
| fLastTaskName = taskName; |
| int locationIndex = msg.indexOf(',', index4 + 1); |
| int finalIndex = locationIndex + 1; |
| String fileName = msg.substring(index4 + 1, locationIndex); |
| int locationLength = 0; |
| if (fileName.length() == 0) { |
| fileName = fLastFileName; |
| } else { |
| finalIndex = msg.indexOf(',', locationIndex) + 1; |
| locationLength = Integer.parseInt(fileName); |
| fileName = msg.substring(finalIndex, finalIndex + locationLength); |
| locationLength += 1; // set past delimiter |
| } |
| fLastFileName = fileName; |
| int lineNumber = Integer.parseInt(msg.substring(finalIndex + locationLength)); |
| int size = AntLaunching.LEFT_COLUMN_SIZE - (taskName.length() + 3); |
| int offset = Math.max(size - 2, 1); |
| int length = AntLaunching.LEFT_COLUMN_SIZE - size - 3; |
| if (fileName != null) { |
| generateLink(line, fileName, lineNumber, offset, length); |
| } |
| |
| StringBuffer fullMessage = new StringBuffer(); |
| adornMessage(taskName, line, fullMessage); |
| writeMessage(fullMessage.append(System.getProperty("line.separator")).toString(), priority); //$NON-NLS-1$ |
| } |
| |
| private void generateLink(String line, String fileName, int lineNumber, int offset, int length) { |
| if (fLaunch != null) { |
| ((AntLaunch) fLaunch).addLinkDescriptor(line, fileName, lineNumber, offset, length); |
| } |
| } |
| |
| /** |
| * Returns the associated process, finding it if necessary. |
| */ |
| protected IProcess getProcess() { |
| if (fProcess == null) { |
| if (fProcessId != null) { |
| for (IProcess process : DebugPlugin.getDefault().getLaunchManager().getProcesses()) { |
| if (fProcessId.equals(process.getAttribute(AbstractEclipseBuildLogger.ANT_PROCESS_ID))) { |
| fProcess = process; |
| break; |
| } |
| } |
| } |
| } |
| return fProcess; |
| } |
| |
| private AntStreamMonitor getMonitor(int priority) { |
| IProcess process = getProcess(); |
| if (process == null) { |
| return null; |
| } |
| AntStreamsProxy proxy = (AntStreamsProxy) process.getStreamsProxy(); |
| if (proxy == null) { |
| return null; |
| } |
| AntStreamMonitor monitor = null; |
| switch (priority) { |
| case Project.MSG_INFO: |
| monitor = (AntStreamMonitor) proxy.getOutputStreamMonitor(); |
| break; |
| case Project.MSG_ERR: |
| monitor = (AntStreamMonitor) proxy.getErrorStreamMonitor(); |
| break; |
| case Project.MSG_DEBUG: |
| monitor = (AntStreamMonitor) proxy.getDebugStreamMonitor(); |
| break; |
| case Project.MSG_WARN: |
| monitor = (AntStreamMonitor) proxy.getWarningStreamMonitor(); |
| break; |
| case Project.MSG_VERBOSE: |
| monitor = (AntStreamMonitor) proxy.getVerboseStreamMonitor(); |
| break; |
| default: |
| break; |
| } |
| return monitor; |
| } |
| |
| /** |
| * Builds a right justified task prefix for the given build event, placing it in the given string buffer. |
| * |
| * @param taskName |
| * the name of the task, can be <code>null</code> |
| * @param line |
| * the line of text |
| * @param fullMessage |
| * buffer to place task prefix in |
| */ |
| private void adornMessage(String taskName, String line, StringBuffer fullMessage) { |
| String tname = taskName; |
| if (tname == null) { |
| tname = "null"; //$NON-NLS-1$ |
| } |
| |
| int size = AntLaunching.LEFT_COLUMN_SIZE - (tname.length() + 6); |
| for (int i = 0; i < size; i++) { |
| fullMessage.append(' '); |
| } |
| |
| fullMessage.append(line); |
| } |
| |
| protected void writeMessage(String message, int priority) { |
| AntStreamMonitor monitor = getMonitor(priority); |
| if (monitor == null) { |
| if (fMessageQueue == null) { |
| fMessageQueue = new ArrayList<>(); |
| } |
| fMessageQueue.add(message); |
| return; |
| } |
| if (fMessageQueue != null) { |
| for (String oldMessage : fMessageQueue) { |
| monitor.append(oldMessage); |
| } |
| fMessageQueue = null; |
| } |
| monitor.append(message); |
| } |
| |
| @Override |
| public void launchesAdded(ILaunch[] launches) { |
| // do nothing |
| } |
| |
| @Override |
| public void launchesChanged(ILaunch[] launches) { |
| // do nothing |
| } |
| |
| @Override |
| public void launchesRemoved(ILaunch[] launches) { |
| for (ILaunch launch : launches) { |
| if (launch.equals(fLaunch)) { |
| shutDown(); |
| return; |
| } |
| } |
| } |
| } |