blob: 91b689105e6fdd13e1c9517a6777726895645317 [file] [log] [blame]
/*******************************************************************************
* 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.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$
}
}