package aQute.remote.main; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.osgi.framework.Constants; import aQute.lib.io.IO; import aQute.libg.shacache.ShaCache; import aQute.libg.shacache.ShaSource; import aQute.remote.util.Link; import aQute.service.reporter.Reporter; /** * Creates a framework and through that framework's class loader it will create * an AgentServer. */ public class EnvoyDispatcher implements Closeable { private ShaCache cache; private ShaSource source; private Reporter main; private File storage; private String network; private int port; private Map<String,DispatcherInfo> frameworks = new HashMap<String,EnvoyDispatcher.DispatcherInfo>(); private Set<EnvoyImpl> envoys = new HashSet<EnvoyImpl>(); class DispatcherInfo { String name; int port; URLClassLoader cl; Class< ? > dispatcher; Map<String,Object> properties; Collection<String> runpath; Closeable framework; File storage; @SuppressWarnings("deprecation") void close() { try { main.trace("closing framework for %s", this); framework.close(); frameworks.remove(name); } catch (Exception e) { main.exception(e, "Closing framework for %s", this); } } public String toString() { return name + "(" + framework + ") [" + port + "]"; } } public class EnvoyImpl implements Envoy { private Link<Envoy,EnvoySupervisor> link; EnvoyImpl(Socket socket) throws IOException { envoys.add(this); socket.setSoTimeout(500); this.link = new Link<Envoy,EnvoySupervisor>(EnvoySupervisor.class, this, socket.getInputStream(), socket.getOutputStream()); setRemote(this.link.getRemote()); } void open() { link.open(); } /* * If the supervisor gets a true on isEnvoy() then it should first * create a framework and then an agent. A supervisor should first try * to create a framework. If this framework already exists or has a * different runpath/properties, we return true. Otherwise, we close a * previous framework under this name and create a new one with the * given props. */ @SuppressWarnings("deprecation") @Override public boolean createFramework(String name, Collection<String> runpath, Map<String,Object> properties) throws Exception { main.trace("create framework %s - %s --- %s", name, runpath, properties); if (!name.matches("[a-zA-Z0-9_.$-]+")) throw new IllegalArgumentException("Name must match symbolic name"); try { DispatcherInfo existing = frameworks.get(name); if (existing != null) { if (existing.runpath.equals(runpath) && existing.properties.equals(properties)) { createAgent(existing, false); return false; } else { existing.close(); frameworks.remove(name); } } DispatcherInfo info = create(name, runpath, properties); frameworks.put(name, info); createAgent(info, true); return true; } catch (Exception e) { main.trace("creating framework %s: %s", name, e); main.exception(e, "creating framework"); throw e; } } @SuppressWarnings("deprecation") private void createAgent(DispatcherInfo info, boolean state) throws Exception { main.trace("Adding an agent for %s", info.name); link.transfer(state); Method toAgent = info.dispatcher.getMethod("toAgent", info.framework.getClass(), DataInputStream.class, DataOutputStream.class); toAgent.invoke(null, info.framework, link.getInput(), link.getOutput()); close(); } @SuppressWarnings("deprecation") private DispatcherInfo create(String name, Collection<String> runpath, Map<String,Object> properties) throws Exception { List<URL> files = new ArrayList<URL>(); for (String sha : runpath) { files.add(cache.getFile(sha, source).toURI().toURL()); } main.trace("runpath %s", files); DispatcherInfo info = new DispatcherInfo(); info.name = name; info.cl = new URLClassLoader(files.toArray(new URL[0])); info.properties = new HashMap<String,Object>(properties); info.runpath = runpath; info.storage = new File(storage, name); info.dispatcher = info.cl.loadClass("aQute.remote.agent.AgentDispatcher"); File storage = new File(EnvoyDispatcher.this.storage, name); IO.mkdirs(storage); if (!storage.isDirectory()) throw new IllegalArgumentException("Cannot create framework storage " + storage); properties.put(Constants.FRAMEWORK_STORAGE, info.storage.getAbsolutePath()); Method newFw = info.dispatcher.getMethod("createFramework", String.class, Map.class, File.class, File.class); info.framework = (Closeable) newFw.invoke(null, name, properties, storage, cache.getRoot()); return info; } @Override public boolean isEnvoy() { return true; } public void close() throws IOException { if (envoys.remove(this) && link != null) link.close(); link = null; } @Override public boolean ping() { return true; } } public EnvoyDispatcher(Reporter main, File cache, File storage, String network, int port) { this.main = main; this.cache = new ShaCache(cache); this.storage = storage; this.network = network; this.port = port; } public void setRemote(final EnvoySupervisor remote) { this.source = new ShaSource() { @Override public boolean isFast() { return false; } @Override public InputStream get(String sha) throws Exception { byte[] data = remote.getFile(sha); if (data == null) return null; return new ByteArrayInputStream(data); } }; } @Override public void close() throws IOException { for (EnvoyImpl envoy : envoys) envoy.close(); for (DispatcherInfo di : frameworks.values()) { di.close(); } } @SuppressWarnings("deprecation") public void run() { while (!Thread.currentThread().isInterrupted()) try { InetAddress address = network.equals("*") ? null : InetAddress.getByName(network); ServerSocket server = address == null ? new ServerSocket(port) : new ServerSocket(port, 3, address); main.trace("Will wait for %s:%s to finish", address, port); while (!Thread.currentThread().isInterrupted()) try { Socket socket = server.accept(); main.trace("Got a request on %s", socket); EnvoyImpl envoyImpl = new EnvoyImpl(socket); envoyImpl.open(); } catch (Exception e) { main.exception(e, "while listening for incoming requests on %s:%s", network, port); break; } server.close(); } catch (Exception e) { try { Thread.sleep(2000); } catch (InterruptedException e1) {} } try { close(); } catch (IOException e) { // } } }