/*******************************************************************************
* Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors
* 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://robocode.sourceforge.net/license/epl-v10.html
*
* Contributors:
* Pavel Savara
* - Initial implementation
*******************************************************************************/
package net.sf.robocode.host.proxies;
import net.sf.robocode.host.events.EventManager;
import net.sf.robocode.host.io.RobotFileSystemManager;
import net.sf.robocode.host.io.RobotOutputStream;
import net.sf.robocode.host.security.RobotThreadManager;
import net.sf.robocode.host.*;
import static net.sf.robocode.io.Logger.logError;
import static net.sf.robocode.io.Logger.logMessage;
import net.sf.robocode.peer.BadBehavior;
import net.sf.robocode.peer.ExecCommands;
import net.sf.robocode.peer.IRobotPeer;
import net.sf.robocode.repository.IRobotRepositoryItem;
import net.sf.robocode.core.Container;
import robocode.RobotStatus;
import robocode.exception.AbortedException;
import robocode.exception.DeathException;
import robocode.exception.DisabledException;
import robocode.exception.WinException;
import robocode.robotinterfaces.IBasicRobot;
import robocode.robotinterfaces.peer.IBasicRobotPeer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author Pavel Savara (original)
*/
public abstract class HostingRobotProxy implements IHostingRobotProxy, IHostedThread {
protected EventManager eventManager;
protected RobotThreadManager robotThreadManager;
protected RobotFileSystemManager robotFileSystemManager;
private final IRobotRepositoryItem robotSpecification;
protected IRobotClassLoader robotClassLoader;
protected final RobotStatics statics;
protected RobotOutputStream out;
protected final IRobotPeer peer;
protected final IHostManager hostManager;
private IThreadManager threadManager;
protected IBasicRobot robot;
private final Set<String> securityViolations = Collections.synchronizedSet(new HashSet<String>());
HostingRobotProxy(IRobotRepositoryItem robotSpecification, IHostManager hostManager, IRobotPeer peer, RobotStatics statics) {
this.peer = peer;
this.statics = statics;
this.hostManager = hostManager;
this.robotSpecification = robotSpecification;
robotClassLoader = getHost(robotSpecification).createLoader(robotSpecification);
robotClassLoader.setRobotProxy(this);
out = new RobotOutputStream();
robotThreadManager = new RobotThreadManager(this);
loadClassBattle();
robotFileSystemManager = new RobotFileSystemManager(this, hostManager.getRobotFilesystemQuota(),
robotSpecification.getWritableDirectory(), robotSpecification.getReadableDirectory(),
robotSpecification.getRootPath());
robotFileSystemManager.initialize();
}
private JavaHost getHost(IRobotRepositoryItem robotSpecification) {
return (JavaHost) Container.cache.getComponent("robocode.host." + robotSpecification.getRobotLanguage());
}
public void cleanup() {
robot = null;
// Remove the file system and the manager
robotFileSystemManager = null;
if (out != null) {
out.close();
out = null;
}
if (robotThreadManager != null) {
robotThreadManager.cleanup();
}
robotThreadManager = null;
// Cleanup and remove class manager
if (robotClassLoader != null) {
robotClassLoader.cleanup();
robotClassLoader = null;
}
}
public RobotOutputStream getOut() {
return out;
}
public void println(String s) {
out.println(s);
}
public void println(Throwable ex) {
ex.printStackTrace(out);
}
public RobotStatics getStatics() {
return statics;
}
public RobotFileSystemManager getRobotFileSystemManager() {
return robotFileSystemManager;
}
public ClassLoader getRobotClassloader() {
return (ClassLoader) robotClassLoader;
}
// -----------
// battle driven methods
// -----------
protected abstract void initializeRound(ExecCommands commands, RobotStatus status);
public void startRound(ExecCommands commands, RobotStatus status) {
initializeRound(commands, status);
threadManager = ((HostManager) hostManager).getThreadManager();
robotThreadManager.start(threadManager);
}
public void forceStopThread() {
if (!robotThreadManager.forceStop()) {
peer.punishBadBehavior(BadBehavior.UNSTOPPABLE);
peer.setRunning(false);
}
}
public void waitForStopThread() {
if (!robotThreadManager.waitForStop()) {
peer.punishBadBehavior(BadBehavior.UNSTOPPABLE);
peer.setRunning(false);
}
}
private void loadClassBattle() {
try {
robotClassLoader.loadRobotMainClass(true);
} catch (Throwable e) {
println("SYSTEM: Could not load " + statics.getName() + " : ");
println(e);
drainEnergy();
}
}
private boolean loadRobotRound() {
robot = null;
try {
threadManager.setLoadingRobot(this);
robot = robotClassLoader.createRobotInstance();
if (robot == null) {
println("SYSTEM: Skipping robot: " + statics.getName());
return false;
}
robot.setOut(out);
robot.setPeer((IBasicRobotPeer) this);
eventManager.setRobot(robot);
} catch (IllegalAccessException e) {
println("SYSTEM: Unable to instantiate this robot: " + e);
println("SYSTEM: Is your constructor marked public?");
println(e);
robot = null;
logError(e);
return false;
} catch (Throwable e) {
println("SYSTEM: An error occurred during initialization of " + statics.getName());
println("SYSTEM: " + e);
println(e);
robot = null;
logError(e);
return false;
} finally {
threadManager.setLoadingRobot(null);
}
return true;
}
protected abstract void executeImpl();
public void run() {
// Only initialize AWT if we are not running in headless mode.
// Bugfix [2833271] IllegalThreadStateException with the AWT-Shutdown thread.
// Read more about headless mode here:
// http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
if (System.getProperty("java.awt.headless", "true").equals("false")) {
robotThreadManager.initAWT();
}
if (robotSpecification.isValid() && loadRobotRound()) {
try {
if (robot != null) {
peer.setRunning(true);
// Process all events for the first turn.
// This is done as the first robot status event must occur before the robot
// has started running.
eventManager.processEvents();
Runnable runnable = robot.getRobotRunnable();
if (runnable != null) {
runnable.run();
}
}
while (peer.isRunning()) {
executeImpl();
}
} catch (WinException e) {// Do nothing
} catch (AbortedException e) {// Do nothing
} catch (DeathException e) {
println("SYSTEM: " + statics.getName() + " has died");
} catch (DisabledException e) {
drainEnergy();
String msg = e.getMessage();
if (msg == null) {
msg = "";
} else {
msg = ": " + msg;
}
println("SYSTEM: Robot disabled" + msg);
logMessage(statics.getName() + "Robot disabled");
} catch (Exception e) {
drainEnergy();
println(e);
logMessage(statics.getName() + ": Exception: " + e); // without stack here
} catch (Throwable t) {
drainEnergy();
if (t instanceof ThreadDeath) {
logMessage(statics.getName() + " stopped successfully.");
} else {
println(t);
logMessage(statics.getName() + ": Throwable: " + t); // without stack here
}
} finally {
waitForBattleEndImpl();
}
} else {
drainEnergy();
peer.punishBadBehavior(BadBehavior.CANNOT_START);
waitForBattleEndImpl();
}
peer.setRunning(false);
// If battle is waiting for us, well, all done!
synchronized (this) {
notifyAll();
}
}
protected abstract void waitForBattleEndImpl();
public void drainEnergy() {
peer.drainEnergy();
}
public void punishSecurityViolation(String message) {
// Prevent unit tests of failing if multiple threads are calling this method in the same time.
// We only want the a specific type of security violation logged once so we only get one error
// per security violation.
synchronized (securityViolations) {
String key = message;
if (key.startsWith("Preventing Thread-")) {
key = key.replaceAll("\\d+", "X");
}
if (!securityViolations.contains(key)) {
securityViolations.add(key);
logError(message);
println("SYSTEM: " + message);
if (securityViolations.size() == 1) {
peer.drainEnergy();
peer.punishBadBehavior(BadBehavior.SECURITY_VIOLATION);
}
}
}
}
}