package xapi.dev.gwt.gui; import xapi.collect.impl.AbstractMultiInitMap; import xapi.dev.gwt.CodeServerGuiOptions; import xapi.log.X_Log; import xapi.log.api.LogLevel; import xapi.util.api.Pair; import xapi.util.impl.PairBuilder; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; @SuppressWarnings("serial") public class CodeServerGui extends JFrame{ private static File tmpConfig; static { try { tmpConfig= File.createTempFile("xapi-config", "xml"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } private SingleFileSelector test; private SourcesSelector sources; private CodeServerControls controls; private ProcessLog logger; public CodeServerGui() { super("XApi Codeserver"); BorderLayout layout = new BorderLayout(5, 5); setLayout(layout); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 500, 300); // setAlwaysOnTop(true); test = new SingleFileSelector("Set Work Directory"); test.setToolTipText("The working directory where gwt codeserver will write compiles. Defaults to "+tmpConfig.getParent()); test.setChooserType(JFileChooser.DIRECTORIES_ONLY); try { test.setFile(tmpConfig.getParentFile()); } catch (Exception e) { X_Log.warn(getClass(), "Error loading file ", tmpConfig.getParentFile(), e); } add(test,BorderLayout.NORTH); controls = new CodeServerControls(new Runnable() { @Override public void run() { launchServer(isUseTestSources(), "JS", getLogLevel()); } }); add(controls,BorderLayout.SOUTH); logger = new ProcessLog(){ Runnable recalc; @Override public void invalidate() { super.invalidate(); if (null==recalc){ recalc = new Runnable() { @Override public void run() { recalc = null; CodeServerGui.this.validate(); } }; SwingUtilities.invokeLater(recalc); } } }; sources = new SourcesSelector("Gwt Sources", logger); JScrollPane wrap = new JScrollPane(logger,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitter.setLeftComponent(sources); splitter.setRightComponent(wrap); splitter.setResizeWeight(0.5); add(splitter,BorderLayout.CENTER); // add(logger,BorderLayout.EAST); } protected String getLogLevel() { return X_Log.logLevel().name(); } protected boolean isUseTestSources() { return false; } protected void launchServer(boolean includeTestSources, String jsInteropMode, String logLevel) { try{ String cpSep = File.pathSeparator; String cp = getClasspath(includeTestSources, cpSep); cp = alterClasspath(cp, cpSep); LinkedList<String> paths = getSourcePaths(includeTestSources); for (String path : paths.toArray(new String[paths.size()])){ File pathFile = new File(path); if (pathFile.exists()){ if (path.endsWith("classes")){ cp += cpSep+pathFile; paths.remove(path); } } } for (File path : classpath){ if (path.getAbsolutePath().endsWith("classes")){ cp = path+cpSep+cp; }else{ cp += cpSep+path; } } X_Log.debug("Codeserver classpath",cp); int debugPort = debugPort(); int len = debugPort > 0 ? 12 : 10; final String[] cmdArray = new String[len]; cmdArray[0] = //path to java executable System.getProperty("java.home")+File.separator+"bin" +File.separator+"java"; cmdArray[1] = "-cp"; cmdArray[2] = cp.replaceAll("(\\s)", "\\\\$1"); int pos = 3; if (debugPort > 0) { cmdArray[pos++] = "-Xdebug"; cmdArray[pos++] = "-agentlib:jdwp=transport=dt_socket,address=localhost:" + debugPort+ ",server=y,suspend=y,timeout="+debugTimeout(); System.out.println("Waiting to attach debugger on port "+debugPort+ " for 10 seconds"); } cmdArray[pos++] = "com.google.gwt.dev.codeserver.CodeServer"; cmdArray[pos++] = "-port"; cmdArray[pos++] = Integer.toString(getPort()); cmdArray[pos++] = "-XjsInteropMode"; cmdArray[pos++] = jsInteropMode; cmdArray[pos++] = "-logLevel"; cmdArray[pos++] = logLevel; String [] srcArray = toCli(paths); len = 1+cmdArray.length+srcArray.length; final String[] exec = new String[len]; pos = cmdArray.length; System.arraycopy(cmdArray, 0, exec, 0, pos); System.arraycopy(srcArray, 0, exec, pos, srcArray.length); exec[exec.length-1]=getModule(); String toRun = Arrays.asList(exec).toString().replaceAll(", ", " "); if (X_Log.loggable(LogLevel.TRACE)) X_Log.trace("exec:\n",toRun.substring(1,toRun.length()-1)); try { Process handle = Runtime.getRuntime().exec(exec); if (debugPort>0) { System.out.println("Not monitoring logs to avoid interfering with debugger"); }else { logger.monitor(handle,getModule()); } if (getWidth() < 1000){ Rectangle b = getBounds(); b.width = 1000; b.height = 600; setBounds(b); } final String module = getModule(); final JPanel wrap = new JPanel(new FlowLayout()); JButton restart = new JButton(new AbstractAction("Kill & Restart") { @Override public void actionPerformed(ActionEvent e) { logger.stop(module); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try{ Process handle = Runtime.getRuntime().exec(exec); logger.monitor(handle,getModule()); }catch (Exception ex) { ex.printStackTrace(); } } }); } }); JButton kill = new JButton(new AbstractAction("Kill") { @Override public void actionPerformed(ActionEvent e) { logger.stop(module); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { controls.remove(wrap); repaint(); } }); } }); wrap.add(restart); wrap.add(kill); controls.add(getModule(),wrap); } catch (IOException e) { e.printStackTrace(); logger.log("Startup failure", e); } }catch (Exception ex) { System.out.println(ex.toString()); ex.printStackTrace(); add(new JLabel(ex.toString()),BorderLayout.WEST); } } /** * The amount of time to wait for the debugger on port {@link #debugPort()} * @return default 10 seconds */ protected int debugTimeout() { return 10000;// ten seconds } protected class GwtFinder extends AbstractMultiInitMap<String, Pair<String, Boolean>, String> { public GwtFinder() { super(PASS_THRU); } @Override protected Pair<String, Boolean> initialize(String cp, String sep) { return findGwt(cp, sep); } public String findArtifact(String cp, String artifact, String cpSep) { if (!cp.contains(artifact)) { if (cpSep == null) cpSep = File.separator; String version = ""; if (!cp.contains(artifact)){ X_Log.info(artifact," was not found in classpath. Trying to guess from existing classpath."); //if the user did not specify codeserver location, let's try to guess it. try{ Pair<String, Boolean> root = get(cp, cpSep); File parent = new File(root.get0()); if (root.get1()) { // In maven } else { // In gwt-dist } // remove .jar and any !classes.on.Classpath String jarName = artifact.split("[.]jar")[0]; if (version.length()>0){ version = version.replace("-", ""); jarName += "-"+version; } jarName += ".jar"; File candidate = new File(parent,jarName); X_Log.trace("Checking if ",candidate," exists"); if (candidate.exists()){ System.out.println("Found " + artifact + " @ "+candidate); cp = cp+cpSep+candidate; }else{ if (version.length()>0){ //we're in a maven directory. First, make sure we're in the correct parent. if (parent.getParentFile().getName().equals(version)){ parent = parent.getParentFile(); } if (parent.getParentFile().getName().equals("gwt-user")){ parent = parent.getParentFile(); } parent = new File(parent,artifact); if (parent.exists()){ parent = new File(parent,version); if (parent.exists()){ candidate = new File(parent,jarName); if (candidate.exists()){ System.out.println("Found " +artifact+" @ "+candidate); cp = cp+cpSep+candidate; } } } } } }catch (Exception ex) { ex.printStackTrace(); } } } return cp; } } protected final GwtFinder gwtLocations = initFinder(); protected Pair<String, Boolean> findGwt(String cp, String cpSep) { String version; for (String chunk0 : cp.split("[:]")){//handle unix for (String chunk1 : chunk0.split("[;]")){//handle windows if ("".equals(chunk1))continue; if (chunk1.contains("gwt-user")){ try{ int ind = chunk1.lastIndexOf("gwt-user"); if (ind<0)continue; File f = new File(chunk1.substring(0, ind)); if (f.exists()){ X_Log.info("Checking for gwt-codeserver from ", f); version = chunk1.substring(ind+8).split(".jar")[0]; String jarName = "gwt-codeserver"; if ( !".jar".equals(version)&&version.length()>0 ){ jarName+=version; } jarName += ".jar"; File jar = new File(f,jarName); if (jar.exists()){ return PairBuilder.pairOf(f.toString(), false); }else{ //check if our gwt-user is in maven, and adjust paths accordingly. File parent = jar.getParentFile(); version = version.replace("-", "");//strip the - from version if (parent.getName().equals(version)){//maven structure; go up two directories parent = new File(parent.getParentFile().getParentFile(),"gwt-codeserver"); if (parent.exists()){ parent = new File(parent,version); if (parent.exists()){ jar = new File(parent,jarName); if (jar.exists()){ return PairBuilder.pairOf(parent.getParentFile().getParent(), true); } } } } } } }catch (Exception ex) { ex.printStackTrace(); } } } } return null; } protected String alterClasspath(String cp, String cpSep) { String gwt = gwtLocations.findArtifact(cp, "gwt-codeserver", cpSep); gwt = gwtLocations.findArtifact(cp, "gwt-dev", cpSep); return gwt; } protected GwtFinder initFinder() { return new GwtFinder(); } protected int debugPort() { return 0;//default in overridden class is 7331; } protected int getPort() { return 1733; } protected String[] toCli(LinkedList<String> sourcePaths) { ArrayList<String> parts = new ArrayList<String>(); for (String path : sourcePaths){ parts.add("-src"); parts.add(path); } return parts.toArray(new String[parts.size()]); } protected final String getModule() { String module=controls.getModule(); if (null==module||module.length()==0){ return getModuleDefault(); } return module; } protected String getModuleDefault() { return "wetheinter.net.Demo"; } public void setModule(String module){ controls.setModule(module); } protected LinkedList<String> getSourcePaths(boolean includeTestSources) { return sources.getSourcePaths(includeTestSources); } protected String getClasspath(boolean includeTestSources, String cpSep) { return sources.getClasspath(includeTestSources, cpSep); } public void addSource(File baseDir) { sources.addSource(baseDir); } public void addTestSource(File baseDir) { sources.addTestSource(baseDir); } public void run(CodeServerGuiOptions opts) { if (opts.isUnload()){ //if the current module is already running, try to stop it //TODO: implement this; return; } //make sure we are showing. if (!isVisible()) setVisible(true); // } private final Set<File> classpath = new LinkedHashSet<File>(); public void addToClasspath(File f) { sources.addSource(f); classpath.add(f); } private final Set<File> testClasspath = new LinkedHashSet<File>(); public void addToTestClasspath(File f) { sources.addTestSource(f); testClasspath.add(f); } }