/* * Copyright (C) 2014 Mark Clarke * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package za.co.jumpingbean.gc.service; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.UUID; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.remote.JMXServiceURL; import za.co.jumpingbean.gc.service.constants.DESC; import za.co.jumpingbean.gc.testApp.GarbageGeneratorApp; public class GeneratorService { final Map<UUID, ProcessObject> processes = new HashMap<>(); ReadWriteLock rwLock = new ReentrantReadWriteLock(); Lock wlock = rwLock.writeLock(); Lock rlock = rwLock.readLock(); public UUID startTestApp(String port, String classPath, String mainClass, List<String> gcOptions) throws IllegalStateException, IOException { ProcessParams params; if (gcOptions == null) { params = new ProcessParams(port, classPath, mainClass); } else { params = new ProcessParams(port, classPath, mainClass, gcOptions); } return this.startTestApp(params); } public UUID connectToJavaProcess(String url, String username, String password) throws IOException { try { ProcessObject procObj = new ProcessObject( GarbageGeneratorApp.class.getCanonicalName(), JMXQueryRunner.createJMXQueryRunner(new JMXServiceURL(url), username, password)); try { wlock.lock(); processes.put(procObj.getId(), procObj); } finally { wlock.unlock(); } return procObj.getId(); } catch (IOException ex) { throw ex; } } public UUID connectToJavaProcess(int pid) throws IOException { try { ProcessObject procObj = new ProcessObject( GarbageGeneratorApp.class.getCanonicalName(), JMXQueryRunner.createJMXQueryRunner(pid)); try { wlock.lock(); processes.put(procObj.getId(), procObj); } finally { wlock.unlock(); } return procObj.getId(); } catch (IOException ex) { throw ex; } } public UUID startTestApp(ProcessParams params) throws IllegalStateException, IOException { System.out.println("Starting up...."); List<String> cmd = new LinkedList<>(); cmd.add("java"); if (params.getClassPath() != null && !params.getClassPath().isEmpty()) { cmd.add("-cp"); cmd.add(params.getClassPath()); } cmd.add("-Dcom.sun.management.jmxremote"); cmd.add("-Dcom.sun.management.jmxremote.port=" + params.getPort()); cmd.add("-Dcom.sun.management.jmxremote.ssl=false"); cmd.add("-Dcom.sun.management.jmxremote.authenticate=false"); cmd.add("-Djava.rmi.server.hostname=127.0.0.1"); if (!params.getGcOptions().isEmpty()) { cmd.addAll(params.getGcOptions()); } cmd.add(params.getMainClass()); ProcessBuilder procBuilder = new ProcessBuilder(cmd); Process proc = procBuilder.start(); Scanner reader = new Scanner(proc.getInputStream()); try { if (proc.exitValue() != 0) { BufferedReader error = new BufferedReader(new InputStreamReader( proc.getErrorStream())); StringBuilder str = new StringBuilder(error.readLine()); String tmp; while ((tmp = error.readLine()) != null) { str.append(tmp); } throw new IllegalStateException("Error starting process: " + str.toString()); } else { throw new IllegalStateException("Process terminate too early."); } } catch (IllegalThreadStateException ex) { //process has not yet exited which is fine. } //Only start stdin reader if proc was launched successfully. //give proc time to start up before //connecting to JMX server synchronized (proc) { try { proc.wait(1000L); } catch (InterruptedException ex) { Logger.getLogger(GeneratorService.class.getName()).log(Level.SEVERE, null, ex); } } //See if proc exits early. It should be still be alive at this point if (proc.isAlive()) { ProcOutputReaderList outputList = new ProcOutputReaderList(proc, reader); outputList.init(); try { ControlableProcess procObj = new ControlableProcess( GarbageGeneratorApp.class.getCanonicalName(), proc, params, outputList, JMXQueryRunner.createJMXQueryRunner(params.getPort())); try { wlock.lock(); processes.put(procObj.getId(), procObj); } finally { wlock.unlock(); } return procObj.getId(); } catch (IOException ex) { outputList.stop(); proc.destroy(); throw new IllegalStateException(ex.getMessage()); } } else { proc.destroy(); throw new IllegalStateException("Process terminate before initialistion could complete."); } } public void stopTestApp(UUID id) { try { wlock.lock(); if (isControlableProcess(id)) { ((ControlableProcess) processes.get(id)).stop(); processes.remove(id); } } finally { wlock.unlock(); } } public void stopAllTestApps() { try { wlock.lock(); for (ProcessObject proc : processes.values()) { if (isControlableProcess(proc.getId())) { ((ControlableProcess) proc).stop(); } } processes.clear(); } finally { wlock.unlock(); } } public JMXQueryRunner getJMXQueryRunner(UUID id) { try { rlock.lock(); return this.processes.get(id).getQry(); } finally { rlock.unlock(); } } public ProcessParams getProcessParams(UUID id) { try { rlock.lock(); if (this.isControlableProcess(id)) { return ((ControlableProcess) this.processes.get(id)).getParams(); } else { return new ProcessParams("", "", ""); } } finally { rlock.unlock(); } } public String getProcessOutput(UUID id) { try { rlock.lock(); if (isControlableProcess(id)) { return ((ControlableProcess) this.processes.get(id)).readProcessOutputLine(); } else { return ""; } } finally { rlock.unlock(); } } public Number queryJMXForValue(UUID id, DESC desc) throws GCExplorerServiceException { Number result = 0; try { rlock.lock(); if (processes.get(id) == null) { throw new GCExplorerServiceException("Process has been removed"); } switch (desc) { case EDENSPACEUSED: result = processes.get(id).getQry().getEdenSpace().getUsage().getUsed(); break; case EDENSPACECOMMITTED: result = processes.get(id).getQry().getEdenSpace().getUsage().getCommitted(); break; case EDENSPACEMAX: result = processes.get(id).getQry().getEdenSpace().getUsage().getMax(); break; case EDENSPACEFREE: result = (processes.get(id).getQry().getEdenSpace(). getUsage().getCommitted() - processes.get(id).getQry().getEdenSpace(). getUsage().getUsed()); break; case SURVIVORSPACEUSED: result = processes.get(id).getQry().getSurvivorSpace().getUsage().getUsed(); break; case SURVIVORSPACECOMMITTED: result = processes.get(id).getQry().getSurvivorSpace().getUsage().getCommitted(); break; case SURVIVORSPACEMAX: result = processes.get(id).getQry().getSurvivorSpace().getUsage().getMax(); break; case SURVIVORSPACEFREE: result = (processes.get(id).getQry().getSurvivorSpace(). getUsage().getCommitted() - processes.get(id).getQry().getSurvivorSpace(). getUsage().getUsed()); break; case OLDGENSPACEUSED: result = processes.get(id).getQry().getOldGenSpace().getUsage().getUsed(); break; case OLDGENSPACECOMMITTED: result = processes.get(id).getQry().getOldGenSpace().getUsage().getCommitted(); break; case OLDGENSPACEMAX: result = processes.get(id).getQry().getOldGenSpace().getUsage().getMax(); break; case OLDGENSPACEFREE: result = (processes.get(id).getQry().getOldGenSpace(). getUsage().getCommitted() - processes.get(id).getQry().getOldGenSpace(). getUsage().getUsed()); break; case PERMGENSPACEUSED: result = processes.get(id).getQry().getPermGenSpace().getUsage().getUsed(); break; case PERMGENSPACECOMMITTED: result = processes.get(id).getQry().getPermGenSpace().getUsage().getCommitted(); break; case PERMGENSPACEMAX: result = processes.get(id).getQry().getPermGenSpace().getUsage().getMax(); break; case PERMGENSPACEFREE: result = (processes.get(id).getQry().getPermGenSpace(). getUsage().getCommitted() - processes.get(id).getQry().getPermGenSpace(). getUsage().getUsed()); break; } } finally { rlock.unlock(); } return result; } public String genLocalInstances(UUID id, int numInstances, int instanceSize, int creationPauseTime) { rlock.lock(); try { String str = processes.get(id).getQry().getGCGenerator().runLocalObjectCreator(numInstances, instanceSize, creationPauseTime); return str; } finally { rlock.unlock(); } } public String genLongLivedInstances(UUID id, int numInstances, int instanceSize, int creationPauseTime) { rlock.lock(); try { String str = processes.get(id).getQry().getGCGenerator().runLongLivedObjectCreator(numInstances, instanceSize, creationPauseTime); return str; } finally { rlock.unlock(); } } public void releaseLongLivedInstances(UUID id, int numInstances, boolean reverse) { rlock.lock(); try { processes.get(id).getQry().getGCGenerator().releaseLongLivedObjects(numInstances, reverse); } finally { rlock.unlock(); } } public String getGCInfo(UUID procId) { rlock.lock(); try { return processes.get(procId).getQry().getGCInfo(); } finally { rlock.unlock(); } } private boolean isControlableProcess(UUID id) { rlock.lock(); try { return processes.get(id) instanceof ControlableProcess; } finally { rlock.unlock(); } } public List<String> getLocalProcessesList() { return LocalJavaProcessFinder.getLocalJavaProcesses(); } public String getJavaVersion(UUID id) { rlock.lock(); try { return processes.get(id).getQry().getJavaVersion(); } finally { rlock.unlock(); } } public String getGCLogEntries(UUID procId) { try { rlock.lock(); return ((ControlableProcess)this.processes.get(procId)).getGCLogEntries(); } finally { rlock.unlock(); } } }