Merge changes I4b879eeb,If2a048df,Iac31374d,I31603a80

* changes:
  Don't put plugins into system classpath
  Isolate plugin class directories from each other
  Display UI build failures in browser
  Unify debug launch configs and auto-detect browser
This commit is contained in:
Shawn Pearce
2013-12-02 15:35:01 +00:00
committed by Gerrit Code Review
12 changed files with 188 additions and 165 deletions

View File

@@ -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.

View File

@@ -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;
}
@@ -556,6 +554,73 @@ 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<GerritLauncher> 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 static ClassLoader useDevClasspath()
throws MalformedURLException, FileNotFoundException {
File out = getDeveloperBuckOut();
List<URL> dirs = new ArrayList<URL>();
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() {
}
}

View File

@@ -94,6 +94,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',

View File

@@ -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;
@@ -26,6 +29,8 @@ 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.gwtexpui.server.CacheHeaders;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
@@ -57,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;
@@ -67,10 +73,10 @@ 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;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Enumeration;
@@ -89,6 +95,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 {
@@ -444,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;
@@ -529,93 +536,78 @@ 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);
}
final File dir = GerritLauncher.getDeveloperBuckOut();
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();
// 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();
}
app.addFilter(new FilterHolder(new Filter() {
private final UserAgentRule rule = new UserAgentRule();
private String lastTarget;
private long lastTime;
if (!dir.getName().equals("classes")) {
throw new FileNotFoundException("Cannot find web root from " + u);
}
dir = dir.getParentFile(); // pop classes
@Override
public void doFilter(ServletRequest request, ServletResponse res,
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");
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;
synchronized (this) {
try {
build(root, gen, rule);
} catch (BuildFailureException e) {
displayFailure(rule, e.why, (HttpServletResponse) res);
return;
}
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();
if (!target.equals(lastTarget) || lastTime != zip.lastModified()) {
lastTarget = target;
lastTime = 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 {
throw new FileNotFoundException("Cannot find web root from " + u);
}
}
chain.doFilter(request, res);
}
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;
}
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("<html><title>BUILD FAILED</title><body>");
w.format("<h1>%s FAILED</h1>", html.escape(rule));
w.write("<pre>");
w.write(html.escape(RawParseUtils.decode(why)));
w.write("</pre>");
w.write("</body></html>");
w.close();
}
@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)
throws IOException {
throws IOException, BuildFailureException {
log.info("buck build " + target);
Properties properties = loadBuckProperties(gen);
String buck = Objects.firstNonNull(properties.getProperty("buck"), "buck");
@@ -643,9 +635,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;
@@ -665,24 +655,12 @@ 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
@SuppressWarnings("serial")
private static class BuildFailureException extends Exception {
final byte[] why;
// 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);
BuildFailureException(byte[] why) {
this.why = why;
}
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-*");
}
}

View File

@@ -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<URL> 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<? extends Module> sysModule = load(sysName, pluginLoader);
Class<? extends Module> sshModule = load(sshName, pluginLoader);
Class<? extends Module> httpModule = load(httpName, pluginLoader);

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/gerrit/gerrit-main/src/main/java/Main.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../test_site"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgerrit.browser=ui_dbg"/>
</launchConfiguration>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/gerrit/gerrit-main/src/main/java/Main.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../test_site"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgerrit.browser=ui_firefox"/>
</launchConfiguration>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/gerrit/gerrit-main/src/main/java/Main.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../test_site"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgerrit.browser=ui_ie9"/>
</launchConfiguration>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/gerrit/gerrit-main/src/main/java/Main.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../test_site"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgerrit.browser=ui_safari"/>
</launchConfiguration>

View File

@@ -9,8 +9,9 @@
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../test_site"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgerrit.browser=ui_chrome"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgerrit.plugin-classes=${resource_loc:/gerrit/buck-out}/eclipse/plugins"/>
</launchConfiguration>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/gerrit/buck-out/gen/lib/gwt/dev/gwt-dev-2.5.0.jar"/>
<listEntry value="/gerrit/buck-out/gen/lib/gwt/dev/gwt-dev-2.5.1.jar"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
@@ -9,6 +9,7 @@
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-startupUrl /&#10;-war ${resource_loc:/gerrit}/buck-out/gen/gerrit-gwtui/ui_dbg__tmp/war&#10;-server com.google.gerrit.gwtdebug.GerritDebugLauncher&#10;com.google.gerrit.GerritGwtUI"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>

View File

@@ -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: