/******************************************************************************* * Copyright (c) 2003, 2016 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ant.internal.launching.remote.logger; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringReader; import java.net.Socket; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Location; import org.apache.tools.ant.Project; import org.apache.tools.ant.Target; import org.apache.tools.ant.util.StringUtils; import org.eclipse.ant.internal.launching.debug.AntDebugState; import org.eclipse.ant.internal.launching.remote.AntSecurityException; import org.eclipse.ant.internal.launching.remote.IAntCoreConstants; import org.eclipse.ant.internal.launching.remote.InternalAntRunner; import org.eclipse.ant.internal.launching.remote.RemoteAntMessages; /** * Parts adapted from org.eclipse.jdt.internal.junit.runner.RemoteTestRunner A build logger that reports via a socket connection. See MessageIds for * more information about the protocol. */ public class RemoteAntBuildLogger extends DefaultLogger { /** Time of the start of the build */ private long fStartTime = System.currentTimeMillis(); /** * The client socket. */ private Socket fEventSocket; /** * Print writer for sending messages */ private PrintWriter fWriter; /** * Host to connect to, default is the localhost */ protected String fHost = IAntCoreConstants.EMPTY_STRING; /** * Port to connect to. */ private int fEventPort = -1; private String fProcessId = null; /** * Is the debug mode enabled? */ protected boolean fDebugMode = false; protected boolean fSentProcessId = false; private List<BuildEvent> fEventQueue; private String fLastFileName = null; private String fLastTaskName = null; /* * (non-Javadoc) * * @see org.apache.tools.ant.DefaultLogger#printMessage(java.lang.String, java.io.PrintStream, int) */ @Override protected void printMessage(String message, PrintStream stream, int priority) { marshalMessage(priority, message); } /** * Connect to the remote Ant build listener. */ protected void connect() { if (fDebugMode) { System.out.println("RemoteAntBuildLogger: trying to connect" + fHost + ":" + fEventPort); //$NON-NLS-1$ //$NON-NLS-2$ } for (int i = 1; i < 5; i++) { try { fEventSocket = new Socket(fHost, fEventPort); fWriter = new PrintWriter(fEventSocket.getOutputStream(), true); return; } catch (IOException e) { // do nothing } try { Thread.sleep(500); } catch (InterruptedException e) { // do nothing } } shutDown(); } /** * Shutdown the connection to the remote build listener. */ protected void shutDown() { if (fEventQueue != null) { fEventQueue.clear(); } if (fWriter != null) { fWriter.close(); fWriter = null; } try { if (fEventSocket != null) { fEventSocket.close(); fEventSocket = null; } } catch (IOException e) { // do nothing } } private void sendMessage(String msg) { if (fWriter == null) { return; } fWriter.println(msg); } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#buildFinished(org.apache.tools.ant.BuildEvent) */ @Override public void buildFinished(BuildEvent event) { if (!fSentProcessId) { establishConnection(); } handleException(event); printMessage(getTimeString(System.currentTimeMillis() - fStartTime), out, Project.MSG_INFO); shutDown(); } protected void handleException(BuildEvent event) { Throwable exception = event.getException(); if (exception == null || exception instanceof AntSecurityException) { return; } StringBuffer message = new StringBuffer(); message.append(StringUtils.LINE_SEP); message.append(RemoteAntMessages.getString("RemoteAntBuildLogger.1")); //$NON-NLS-1$ message.append(StringUtils.LINE_SEP); if (Project.MSG_VERBOSE <= this.msgOutputLevel || !(exception instanceof BuildException)) { message.append(StringUtils.getStackTrace(exception)); } else { if (exception instanceof BuildException) { message.append(exception.toString()).append(StringUtils.LINE_SEP); } else { message.append(exception.getMessage()).append(StringUtils.LINE_SEP); } } message.append(StringUtils.LINE_SEP); printMessage(message.toString(), out, Project.MSG_ERR); } private String getTimeString(long milliseconds) { long seconds = milliseconds / 1000; long minutes = seconds / 60; seconds = seconds % 60; StringBuffer result = new StringBuffer(RemoteAntMessages.getString("RemoteAntBuildLogger.Total_time")); //$NON-NLS-1$ if (minutes > 0) { result.append(minutes); if (minutes > 1) { result.append(RemoteAntMessages.getString("RemoteAntBuildLogger._minutes_2")); //$NON-NLS-1$ } else { result.append(RemoteAntMessages.getString("RemoteAntBuildLogger._minute_3")); //$NON-NLS-1$ } } if (seconds > 0) { if (minutes > 0) { result.append(' '); } result.append(seconds); if (seconds > 1) { result.append(RemoteAntMessages.getString("RemoteAntBuildLogger._seconds_4")); //$NON-NLS-1$ } else { result.append(RemoteAntMessages.getString("RemoteAntBuildLogger._second_5")); //$NON-NLS-1$ } } if (seconds == 0 && minutes == 0) { result.append(milliseconds); result.append(RemoteAntMessages.getString("RemoteAntBuildLogger._milliseconds_6")); //$NON-NLS-1$ } return result.toString(); } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#targetStarted(org.apache.tools.ant.BuildEvent) */ @Override public void targetStarted(BuildEvent event) { if (!fSentProcessId) { establishConnection(); } if (Project.MSG_INFO <= msgOutputLevel) { marshalTargetMessage(event); } } protected void establishConnection() { if (fEventPort != -1) { connect(); } else { shutDown(); return; } fSentProcessId = true; StringBuffer message = new StringBuffer(MessageIds.PROCESS_ID); message.append(fProcessId); sendMessage(message.toString()); if (fEventQueue != null) { for (Iterator<BuildEvent> iter = fEventQueue.iterator(); iter.hasNext();) { processEvent(iter.next()); } fEventQueue = null; } } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#messageLogged(org.apache.tools.ant.BuildEvent) */ @SuppressWarnings("unused") @Override public void messageLogged(BuildEvent event) { if (event.getPriority() > msgOutputLevel && event.getPriority() != InternalAntRunner.MSG_PROJECT_HELP) { return; } if (!fSentProcessId) { if (event.getPriority() == InternalAntRunner.MSG_PROJECT_HELP) { if (Project.MSG_INFO > msgOutputLevel) { return; } // no buildstarted or project started for project help option establishConnection(); return; } if (fEventQueue == null) { fEventQueue = new ArrayList<BuildEvent>(10); } fEventQueue.add(event); return; } processEvent(event); } private void processEvent(BuildEvent event) { if (event.getTask() != null & !emacsMode) { try { marshalTaskMessage(event); } catch (IOException e) { // do nothing } } else { marshalMessage(event); } } private void marshalMessage(BuildEvent event) { String eventMessage = event.getMessage(); if (eventMessage.length() == 0) { return; } marshalMessage(event.getPriority(), eventMessage); } protected void marshalMessage(int priority, String message) { try { BufferedReader r = new BufferedReader(new StringReader(message)); String line = r.readLine(); StringBuffer messageLine; while (line != null) { messageLine = new StringBuffer(); if (priority != -1) { messageLine.append(priority); messageLine.append(','); } messageLine.append(line); sendMessage(messageLine.toString()); line = r.readLine(); } } catch (IOException e) { // do nothing } } private void marshalTaskMessage(BuildEvent event) throws IOException { String eventMessage = event.getMessage(); if (eventMessage.length() == 0) { return; } BufferedReader r = new BufferedReader(new StringReader(eventMessage)); String line = r.readLine(); StringBuffer message; String taskName = event.getTask().getTaskName(); if (taskName != null && taskName.equals(fLastTaskName)) { taskName = IAntCoreConstants.EMPTY_STRING; } else { fLastTaskName = taskName; } Location location = event.getTask().getLocation(); String fileName = null; int lineNumber = -1; try { fileName = location.getFileName(); lineNumber = location.getLineNumber(); } catch (NoSuchMethodError e) { // older Ant fileName = location.toString(); } if (location.equals(Location.UNKNOWN_LOCATION)) { fileName = location.toString(); lineNumber = -1; } int priority = event.getPriority(); while (line != null) { message = new StringBuffer(MessageIds.TASK); message.append(priority); message.append(','); message.append(taskName); message.append(','); message.append(line.length()); message.append(','); message.append(line); message.append(','); if (!fileName.equals(fLastFileName)) { message.append(fileName.length()); message.append(','); message.append(fileName); } message.append(','); message.append(lineNumber); sendMessage(message.toString()); fLastFileName = fileName; line = r.readLine(); } } private void marshalTargetMessage(BuildEvent event) { Target target = event.getTarget(); Location location = AntDebugState.getLocation(target); StringBuffer message = new StringBuffer(); message.append(MessageIds.TARGET); message.append(','); message.append(target.getName()); message.append(':'); message.append(','); if (location != null && location != Location.UNKNOWN_LOCATION) { // if a target has a valid location then we are on an Ant that is // new enough to have the accessor methods on Location String fileName = location.getFileName(); message.append(fileName.length()); message.append(','); message.append(fileName); message.append(','); message.append(location.getLineNumber()); } sendMessage(message.toString()); } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#buildStarted(org.apache.tools.ant.BuildEvent) */ @Override public void buildStarted(BuildEvent event) { establishConnection(); super.buildStarted(event); } public void configure(Map<String, String> userProperties) { String portProperty = userProperties.remove("eclipse.connect.port"); //$NON-NLS-1$ if (portProperty != null) { fEventPort = Integer.parseInt(portProperty); } fProcessId = userProperties.remove("org.eclipse.ant.core.ANT_PROCESS_ID"); //$NON-NLS-1$ } }