From cda2121828edf96f8ba68b39f65a95ee23eecea3 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Thu, 28 Nov 2013 20:49:42 -0800 Subject: [PATCH 1/4] Unify debug launch configs and auto-detect browser Use the same permutation selector applied by the server at runtime to automatically detect the browser making the request and build/refresh that UI permutation. Change-Id: I31603a80e23a8e4e2c46325be4dac808d8f98a5c --- Documentation/dev-eclipse.txt | 2 +- gerrit-pgm/BUCK | 1 + .../gerrit/pgm/http/jetty/JettyServer.java | 117 +++++++----------- tools/eclipse/buck_daemon_ui_dbg.launch | 13 -- tools/eclipse/buck_daemon_ui_firefox.launch | 13 -- tools/eclipse/buck_daemon_ui_ie9.launch | 13 -- tools/eclipse/buck_daemon_ui_safari.launch | 13 -- ..._ui_chrome.launch => gerrit_daemon.launch} | 2 +- ...t_debug.launch => gerrit_gwt_debug.launch} | 3 +- 9 files changed, 50 insertions(+), 127 deletions(-) delete mode 100644 tools/eclipse/buck_daemon_ui_dbg.launch delete mode 100644 tools/eclipse/buck_daemon_ui_firefox.launch delete mode 100644 tools/eclipse/buck_daemon_ui_ie9.launch delete mode 100644 tools/eclipse/buck_daemon_ui_safari.launch rename tools/eclipse/{buck_daemon_ui_chrome.launch => gerrit_daemon.launch} (89%) rename tools/eclipse/{buck_gwt_debug.launch => gerrit_gwt_debug.launch} (89%) diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt index eeee5a3f1c..50236a20f0 100644 --- a/Documentation/dev-eclipse.txt +++ b/Documentation/dev-eclipse.txt @@ -37,7 +37,7 @@ Running the Daemon Duplicate the existing launch configuration: * Run -> Debug Configurations ... -* Java Application -> `buck_daemon_ui_*` +* Java Application -> `gerrit_daemon` * Right click, Duplicate * Modify the name to be unique. diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK index 67b28ac80a..2d84af7817 100644 --- a/gerrit-pgm/BUCK +++ b/gerrit-pgm/BUCK @@ -90,6 +90,7 @@ java_library2( '//gerrit-cache-h2:cache-h2', '//gerrit-common:server', '//gerrit-extension-api:api', + '//gerrit-gwtexpui:linker_server', '//gerrit-gwtexpui:server', '//gerrit-httpd:httpd', '//gerrit-lucene:lucene', diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index 4a191099c4..c5c812a300 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java @@ -26,6 +26,7 @@ import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.util.TimeUtil; +import com.google.gwtexpui.linker.server.UserAgentRule; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Singleton; @@ -558,60 +559,53 @@ public class JettyServer { } dir = dir.getParentFile(); // pop classes - if ("buck-out".equals(dir.getName())) { - final File dstwar = makeWarTempDir(); - String pkg = "gerrit-gwtui"; - String target = targetForBrowser(System.getProperty("gerrit.browser")); - final File gen = new File(dir, "gen"); - String out = new File(new File(gen, pkg), target).getAbsolutePath(); - final File zip = new File(out + ".zip"); - final File root = dir.getParentFile(); - final String name = "//" + pkg + ":" + target; - - File ui = new File(dstwar, "gerrit_ui"); - File p = new File(ui, "permutations"); - mkdir(ui); - p.createNewFile(); - p.deleteOnExit(); - - app.addFilter(new FilterHolder(new Filter() { - private long last; - - @Override - public void doFilter(ServletRequest request, ServletResponse res, - FilterChain chain) throws IOException, ServletException { - HttpServletRequest req = (HttpServletRequest) request; - build(root, gen, name); - if (last != zip.lastModified()) { - last = zip.lastModified(); - unpack(zip, dstwar); - } - chain.doFilter(req, res); - } - - @Override - public void init(FilterConfig config) { - } - @Override - public void destroy() { - } - }), "/", EnumSet.of(DispatcherType.REQUEST)); - return Resource.newResource(dstwar.toURI()); - } else if ("target".equals(dir.getName())) { - return useMavenDeveloperBuild(dir); - } else { + if (!"buck-out".equals(dir.getName())) { throw new FileNotFoundException("Cannot find web root from " + u); } - } - private static String targetForBrowser(String browser) { - if (browser == null || browser.isEmpty()) { - return "ui_dbg"; - } else if (browser.startsWith("ui_")) { - return browser; - } else { - return "ui_" + browser; - } + final File gen = new File(dir, "gen"); + final File root = dir.getParentFile(); + final File dstwar = makeWarTempDir(); + File ui = new File(dstwar, "gerrit_ui"); + File p = new File(ui, "permutations"); + mkdir(ui); + p.createNewFile(); + p.deleteOnExit(); + + app.addFilter(new FilterHolder(new Filter() { + private final UserAgentRule rule = new UserAgentRule(); + private String lastTarget; + private long lastTime; + + @Override + public void doFilter(ServletRequest request, ServletResponse res, + FilterChain chain) throws IOException, ServletException { + String pkg = "gerrit-gwtui"; + String target = "ui_" + rule.select((HttpServletRequest) request); + File zip = new File(new File(gen, pkg), target + ".zip"); + + synchronized (this) { + build(root, gen, "//" + pkg + ":" + target); + + if (!target.equals(lastTarget) || lastTime != zip.lastModified()) { + lastTarget = target; + lastTime = zip.lastModified(); + unpack(zip, dstwar); + } + } + + chain.doFilter(request, res); + } + + @Override + public void init(FilterConfig config) { + } + + @Override + public void destroy() { + } + }), "/", EnumSet.of(DispatcherType.REQUEST)); + return Resource.newResource(dstwar.toURI()); } private static void build(File root, File gen, String target) @@ -664,25 +658,4 @@ public class JettyServer { } return properties; } - - private Resource useMavenDeveloperBuild(File dir) throws IOException { - dir = dir.getParentFile(); // pop target - dir = dir.getParentFile(); // pop the module we are in - - // Drop down into gerrit-gwtui to find the WAR assets we need. - // - dir = new File(new File(dir, "gerrit-gwtui"), "target"); - final File[] entries = dir.listFiles(); - if (entries == null) { - throw new FileNotFoundException("No " + dir); - } - for (File e : entries) { - if (e.isDirectory() /* must be a directory */ - && e.getName().startsWith("gerrit-gwtui-") - && new File(e, "gerrit_ui/gerrit_ui.nocache.js").isFile()) { - return Resource.newResource(e.toURI()); - } - } - throw new FileNotFoundException("No " + dir + "/gerrit-gwtui-*"); - } } diff --git a/tools/eclipse/buck_daemon_ui_dbg.launch b/tools/eclipse/buck_daemon_ui_dbg.launch deleted file mode 100644 index a345f8afa2..0000000000 --- a/tools/eclipse/buck_daemon_ui_dbg.launch +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tools/eclipse/buck_daemon_ui_firefox.launch b/tools/eclipse/buck_daemon_ui_firefox.launch deleted file mode 100644 index 383b0516d4..0000000000 --- a/tools/eclipse/buck_daemon_ui_firefox.launch +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tools/eclipse/buck_daemon_ui_ie9.launch b/tools/eclipse/buck_daemon_ui_ie9.launch deleted file mode 100644 index 18863e728c..0000000000 --- a/tools/eclipse/buck_daemon_ui_ie9.launch +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tools/eclipse/buck_daemon_ui_safari.launch b/tools/eclipse/buck_daemon_ui_safari.launch deleted file mode 100644 index 55259a9110..0000000000 --- a/tools/eclipse/buck_daemon_ui_safari.launch +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tools/eclipse/buck_daemon_ui_chrome.launch b/tools/eclipse/gerrit_daemon.launch similarity index 89% rename from tools/eclipse/buck_daemon_ui_chrome.launch rename to tools/eclipse/gerrit_daemon.launch index efe26239d4..c57a368bcb 100644 --- a/tools/eclipse/buck_daemon_ui_chrome.launch +++ b/tools/eclipse/gerrit_daemon.launch @@ -9,8 +9,8 @@ + - diff --git a/tools/eclipse/buck_gwt_debug.launch b/tools/eclipse/gerrit_gwt_debug.launch similarity index 89% rename from tools/eclipse/buck_gwt_debug.launch rename to tools/eclipse/gerrit_gwt_debug.launch index 1723cbfdb3..3526e4426c 100644 --- a/tools/eclipse/buck_gwt_debug.launch +++ b/tools/eclipse/gerrit_gwt_debug.launch @@ -1,7 +1,7 @@ - + @@ -9,6 +9,7 @@ + From 80d9cdbb8847c6c75930933bebb78fde96f7b33d Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Fri, 29 Nov 2013 18:28:08 -0800 Subject: [PATCH 2/4] Display UI build failures in browser Instead of crashing the Jetty daemon when there is a UI build failure, keep the daemon running and display the output from Buck in the web browser. This allows for a slightly quicker fix and retry cycle, especially for minor typos in ui.xml files. Simply edit the offending source and reload, without needing to relaunch the daemon. Change-Id: Iac31374d4efd44c4c6a58ce08497fbf24dd6dfa9 --- .../gerrit/pgm/http/jetty/JettyServer.java | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index c5c812a300..3d284e98d5 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java @@ -17,7 +17,10 @@ package com.google.gerrit.pgm.http.jetty; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import com.google.common.base.Charsets; import com.google.common.base.Objects; +import com.google.common.escape.Escaper; +import com.google.common.html.HtmlEscapers; import com.google.common.io.ByteStreams; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.launcher.GerritLauncher; @@ -27,6 +30,7 @@ import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.util.TimeUtil; import com.google.gwtexpui.linker.server.UserAgentRule; +import com.google.gwtexpui.server.CacheHeaders; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Singleton; @@ -58,6 +62,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.util.RawParseUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,6 +73,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; +import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -90,6 +96,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; @Singleton public class JettyServer { @@ -582,10 +589,16 @@ public class JettyServer { FilterChain chain) throws IOException, ServletException { String pkg = "gerrit-gwtui"; String target = "ui_" + rule.select((HttpServletRequest) request); + String rule = "//" + pkg + ":" + target; File zip = new File(new File(gen, pkg), target + ".zip"); synchronized (this) { - build(root, gen, "//" + pkg + ":" + target); + try { + build(root, gen, rule); + } catch (BuildFailureException e) { + displayFailure(rule, e.why, (HttpServletResponse) res); + return; + } if (!target.equals(lastTarget) || lastTime != zip.lastModified()) { lastTarget = target; @@ -597,6 +610,24 @@ public class JettyServer { chain.doFilter(request, res); } + private void displayFailure(String rule, byte[] why, HttpServletResponse res) + throws IOException { + res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + res.setContentType("text/html"); + res.setCharacterEncoding(Charsets.UTF_8.name()); + CacheHeaders.setNotCacheable(res); + + Escaper html = HtmlEscapers.htmlEscaper(); + PrintWriter w = res.getWriter(); + w.write("BUILD FAILED"); + w.format("

%s FAILED

", html.escape(rule)); + w.write("
");
+        w.write(html.escape(RawParseUtils.decode(why)));
+        w.write("
"); + w.write(""); + w.close(); + } + @Override public void init(FilterConfig config) { } @@ -609,7 +640,7 @@ public class JettyServer { } private static void build(File root, File gen, String target) - throws IOException { + throws IOException, BuildFailureException { log.info("buck build " + target); Properties properties = loadBuckProperties(gen); String buck = Objects.firstNonNull(properties.getProperty("buck"), "buck"); @@ -637,9 +668,7 @@ public class JettyServer { throw new InterruptedIOException("interrupted waiting for " + buck); } if (status != 0) { - System.err.write(out); - System.err.println(); - System.exit(status); + throw new BuildFailureException(out); } long time = TimeUtil.nowMs() - start; @@ -658,4 +687,13 @@ public class JettyServer { } return properties; } + + @SuppressWarnings("serial") + private static class BuildFailureException extends Exception { + final byte[] why; + + BuildFailureException(byte[] why) { + this.why = why; + } + } } From 9e4d5aa347fa27edacaf9cfaa8b301ec116fee79 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 30 Nov 2013 01:14:34 -0800 Subject: [PATCH 3/4] Isolate plugin class directories from each other Configure Eclipse to compile/copy each plugin's classes and resources into its own private classes folder. This should fix the duplicate resource warnings for about.md or other documentation and static image assets. Move test and utility classes into their own directories too. This has no real impact on the runtime classpath at this time, but opens the door to do something more creative later. Change-Id: If2a048dfe0349f671a17f9e80d6f22a69a5c0ade --- .../gerrit/launcher/GerritLauncher.java | 44 +++++++++++++++++++ .../gerrit/pgm/http/jetty/JettyServer.java | 37 +--------------- tools/eclipse/project.py | 28 +++++++++--- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java index f7e5ba2ec4..aa19c4cc55 100644 --- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java +++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java @@ -556,6 +556,50 @@ public final class GerritLauncher { } } + /** + * Locate the path of the {@code buck-out} directory in a source tree. + * + * @throws FileNotFoundException if the directory cannot be found. + */ + public static File getDeveloperBuckOut() throws FileNotFoundException { + // Find ourselves in the CLASSPATH, we should be a loose class file. + Class self = GerritLauncher.class; + URL u = self.getResource(self.getSimpleName() + ".class"); + if (u == null) { + throw new FileNotFoundException("Cannot find class " + self.getName()); + } else if (!"file".equals(u.getProtocol())) { + throw new FileNotFoundException("Cannot find extract path from " + u); + } + + // Pop up to the top level classes folder that contains us. + File dir = new File(u.getPath()); + String myName = self.getName(); + for (;;) { + int dot = myName.lastIndexOf('.'); + if (dot < 0) { + dir = dir.getParentFile(); + break; + } + myName = myName.substring(0, dot); + dir = dir.getParentFile(); + } + + dir = popdir(u, dir, "classes"); + dir = popdir(u, dir, "eclipse"); + if ("buck-out".equals(dir.getName())) { + return dir; + } + throw new FileNotFoundException("Cannot find buck-out from " + u); + } + + private static File popdir(URL u, File dir, String name) + throws FileNotFoundException { + if (dir.getName().equals(name)) { + return dir.getParentFile(); + } + throw new FileNotFoundException("Cannot find buck-out from " + u); + } + private GerritLauncher() { } } diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index 3d284e98d5..a57733712a 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java @@ -77,7 +77,6 @@ import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.util.ArrayList; import java.util.EnumSet; import java.util.Enumeration; @@ -452,7 +451,7 @@ public class JettyServer { try { baseResource = unpackWar(GerritLauncher.getDistributionArchive()); } catch (FileNotFoundException err) { - if (err.getMessage() == GerritLauncher.NOT_ARCHIVED) { + if (GerritLauncher.NOT_ARCHIVED.equals(err.getMessage())) { baseResource = useDeveloperBuild(app); } else { throw err; @@ -537,39 +536,7 @@ public class JettyServer { private Resource useDeveloperBuild(ServletContextHandler app) throws IOException { - // Find ourselves in the CLASSPATH. We should be a loose class file. - // - URL u = getClass().getResource(getClass().getSimpleName() + ".class"); - if (u == null) { - throw new FileNotFoundException("Cannot find web application root"); - } - if (!"file".equals(u.getProtocol())) { - throw new FileNotFoundException("Cannot find web root from " + u); - } - - // Pop up to the top level classes folder that contains us. - // - File dir = new File(u.getPath()); - String myName = getClass().getName(); - for (;;) { - int dot = myName.lastIndexOf('.'); - if (dot < 0) { - dir = dir.getParentFile(); - break; - } - myName = myName.substring(0, dot); - dir = dir.getParentFile(); - } - - if (!dir.getName().equals("classes")) { - throw new FileNotFoundException("Cannot find web root from " + u); - } - dir = dir.getParentFile(); // pop classes - - if (!"buck-out".equals(dir.getName())) { - throw new FileNotFoundException("Cannot find web root from " + u); - } - + final File dir = GerritLauncher.getDeveloperBuckOut(); final File gen = new File(dir, "gen"); final File root = dir.getParentFile(); final File dstwar = makeWarTempDir(); diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py index 6e02c35a11..50644ebe2d 100755 --- a/tools/eclipse/project.py +++ b/tools/eclipse/project.py @@ -72,12 +72,14 @@ def gen_classpath(): impl = minidom.getDOMImplementation() return impl.createDocument(None, 'classpath', None) - def classpathentry(kind, path, src=None): + def classpathentry(kind, path, src=None, out=None): e = doc.createElement('classpathentry') e.setAttribute('kind', kind) e.setAttribute('path', path) if src: e.setAttribute('sourcepath', src) + if out: + e.setAttribute('output', out) doc.documentElement.appendChild(e) doc = make_classpath() @@ -112,16 +114,29 @@ def gen_classpath(): gwt_src.add(m.group(1)) for s in sorted(src): + out = None + + if s.startswith('lib/'): + out = 'buck-out/eclipse/lib' + elif s.startswith('plugins/'): + out = 'buck-out/eclipse/' + s + p = path.join(s, 'java') if path.exists(p): - classpathentry('src', p) + classpathentry('src', p, out=out) continue for env in ['main', 'test']: + o = None + if out: + o = out + '/' + env + elif env == 'test': + o = 'buck-out/eclipse/test' + for srctype in ['java', 'resources']: p = path.join(s, 'src', env, srctype) if path.exists(p): - classpathentry('src', p) + classpathentry('src', p, out=o) for libs in [lib, gwt_lib]: for j in sorted(libs): @@ -133,14 +148,15 @@ def gen_classpath(): classpathentry('lib', j, s) for s in sorted(gwt_src): - classpathentry('lib', path.join(ROOT, s, 'src', 'main', 'java')) + p = path.join(ROOT, s, 'src', 'main', 'java') + classpathentry('lib', p, out='buck-out/eclipse/gwtsrc') classpathentry('con', JRE) - classpathentry('output', 'buck-out/classes') + classpathentry('output', 'buck-out/eclipse/classes') p = path.join(ROOT, '.classpath') with open(p, 'w') as fd: - doc.writexml(fd, addindent=' ', newl='\n', encoding='UTF-8') + doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8') try: if args.src: From 130afbd65ec7b4ecbe19cb57c0152fb0201c45f1 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 30 Nov 2013 01:14:45 -0800 Subject: [PATCH 4/4] Don't put plugins into system classpath Plugin code is often loaded into the workspace in Eclipse, and is therefore part of the default project path. Detect this at startup time by looking for gerrit.buck-out system property and building a new classpath consisting only of the primary classes and JARs. The GWT compiler, tests and plugins are excluded from the classpath, preventing weird collisions with the dynamically loaded plugins from $site_path/plugins. PluginLoader recognizes the split output directory used by Eclipse and loads the latest classes files ahead of anything from the JAR in $site_path/plugins. This makes development of most code changes quick by hiding the staleness of the installed JAR. Change-Id: I4b879eebbcb332384c4e747d3f0c4b5c948c5fed --- .../gerrit/launcher/GerritLauncher.java | 29 ++++++++++++++++--- .../gerrit/server/plugins/PluginLoader.java | 19 ++++++++++-- tools/eclipse/gerrit_daemon.launch | 1 + 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java index aa19c4cc55..fb2f254fdc 100644 --- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java +++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java @@ -29,6 +29,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.JarURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; @@ -192,10 +193,7 @@ public final class GerritLauncher { path = getDistributionArchive(); } catch (FileNotFoundException e) { if (NOT_ARCHIVED.equals(e.getMessage())) { - // Assume the CLASSPATH was made complete by the calling process, - // as we are likely being run from within a developer's IDE. - // - return GerritLauncher.class.getClassLoader(); + return useDevClasspath(); } throw e; } @@ -600,6 +598,29 @@ public final class GerritLauncher { throw new FileNotFoundException("Cannot find buck-out from " + u); } + private static ClassLoader useDevClasspath() + throws MalformedURLException, FileNotFoundException { + File out = getDeveloperBuckOut(); + List dirs = new ArrayList(); + dirs.add(new File(new File(out, "eclipse"), "classes").toURI().toURL()); + ClassLoader cl = GerritLauncher.class.getClassLoader(); + for (URL u : ((URLClassLoader) cl).getURLs()) { + if (includeJar(u)) { + dirs.add(u); + } + } + return new URLClassLoader( + dirs.toArray(new URL[dirs.size()]), + ClassLoader.getSystemClassLoader().getParent()); + } + + private static boolean includeJar(URL u) { + String path = u.getPath(); + return path.endsWith(".jar") + && !path.endsWith("-src.jar") + && !path.contains("/buck-out/gen/lib/gwt/"); + } + private GerritLauncher() { } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java index 4682d2bb3b..2eda5d022c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java @@ -504,9 +504,22 @@ public class PluginLoader implements LifecycleListener { Plugin.ApiType.PLUGIN)); } - URL[] urls = {tmp.toURI().toURL()}; - ClassLoader parentLoader = parentFor(type); - ClassLoader pluginLoader = new URLClassLoader(urls, parentLoader); + List urls = new ArrayList<>(2); + String overlay = System.getProperty("gerrit.plugin-classes"); + if (overlay != null) { + File classes = new File(new File(new File(overlay), name), "main"); + if (classes.isDirectory()) { + log.info(String.format( + "plugin %s: including %s", + name, classes.getPath())); + urls.add(classes.toURI().toURL()); + } + } + urls.add(tmp.toURI().toURL()); + + ClassLoader pluginLoader = new URLClassLoader( + urls.toArray(new URL[urls.size()]), + parentFor(type)); Class sysModule = load(sysName, pluginLoader); Class sshModule = load(sshName, pluginLoader); Class httpModule = load(httpName, pluginLoader); diff --git a/tools/eclipse/gerrit_daemon.launch b/tools/eclipse/gerrit_daemon.launch index c57a368bcb..51e4a80360 100644 --- a/tools/eclipse/gerrit_daemon.launch +++ b/tools/eclipse/gerrit_daemon.launch @@ -13,4 +13,5 @@ +