From 87e2a7e8d5ac995a05b82c1e7839285102c111d0 Mon Sep 17 00:00:00 2001 From: ikedam Date: Tue, 10 Dec 2013 23:53:38 +0900 Subject: [PATCH 01/36] [FIXED JENKINS-19976] Enable plugins to load classes from optionally depending plugins even without restarting after depended ones installed. --- .../java/hudson/ClassicPluginStrategy.java | 44 +++++++++++++++++++ core/src/main/java/hudson/PluginManager.java | 17 +++++++ core/src/main/java/hudson/PluginStrategy.java | 8 ++++ 3 files changed, 69 insertions(+) diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index f60166f34e..0cedd1ea25 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -377,6 +377,45 @@ public class ClassicPluginStrategy implements PluginStrategy { plugin.getPlugin().start(); } + /** + * Called when a plugin is deployed, and there is a plugin optionally depending on that plugin. + * The class loader of the existing depending plugin should be updated + * to load classes from the newly deployed plugin. + * + * @param depender the plugin to update its class loader + * @param dependee + * @see hudson.PluginStrategy#updateDependency(hudson.PluginWrapper, hudson.PluginWrapper) + */ + @Override + public void updateDependency(PluginWrapper depender, PluginWrapper dependee) { + DependencyClassLoader classLoader = findAncestorDependencyClassLoader(depender.classLoader); + if (classLoader != null) { + classLoader.updateTransientDependencies(); + LOGGER.log(Level.INFO, "Updated dependency of {0}", depender.getShortName()); + } + } + + private DependencyClassLoader findAncestorDependencyClassLoader(ClassLoader classLoader) + { + for (; classLoader != null; classLoader = classLoader.getParent()) { + if (classLoader instanceof DependencyClassLoader) { + return (DependencyClassLoader)classLoader; + } + + if (classLoader instanceof AntClassLoader) { + // AntClassLoaders hold parents not only as AntClassLoader#getParent() + // but also as AntClassLoader#getConfiguredParent() + DependencyClassLoader ret = findAncestorDependencyClassLoader( + ((AntClassLoader)classLoader).getConfiguredParent() + ); + if (ret != null) { + return ret; + } + } + } + return null; + } + private static File resolve(File base, String relative) { File rel = new File(relative); if(rel.isAbsolute()) @@ -523,6 +562,11 @@ public class ClassicPluginStrategy implements PluginStrategy { this.dependencies = dependencies; } + private void updateTransientDependencies() { + // This will be recalculated at the next time. + transientDependencies = null; + } + private List getTransitiveDependencies() { if (transientDependencies==null) { CyclicGraphDetector cgd = new CyclicGraphDetector() { diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index dc657a9a0b..64371a5dd4 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -450,6 +450,23 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } catch (ReactorException e) { throw new IOException("Failed to initialize "+ sn +" plugin",e); } + + // recalculate dependencies of plugins optionally depending the newly deployed one. + for (PluginWrapper depender: plugins) { + if (depender.equals(p)) { + // skip itself. + continue; + } + for (Dependency d: depender.getOptionalDependencies()) { + if (d.shortName.equals(p.getShortName())) { + // this plugin depends on the newly loaded one! + // recalculate dependencies! + getPluginStrategy().updateDependency(depender, p); + break; + } + } + } + LOGGER.info("Plugin " + sn + " dynamically installed"); } diff --git a/core/src/main/java/hudson/PluginStrategy.java b/core/src/main/java/hudson/PluginStrategy.java index 6024c3571f..d61e23732a 100644 --- a/core/src/main/java/hudson/PluginStrategy.java +++ b/core/src/main/java/hudson/PluginStrategy.java @@ -77,4 +77,12 @@ public interface PluginStrategy extends ExtensionPoint { * @since 1.400 */ List> findComponents(Class type, Hudson hudson); + + /** + * Called when a plugin that is depended by another plugin is newly deployed. + * + * @param depender plugin depending on dependee. + * @param dependee newly loaded plugin. + */ + void updateDependency(PluginWrapper depender, PluginWrapper dependee); } From 8fde2e18a3a6da843d7719844e1fdd737e652c74 Mon Sep 17 00:00:00 2001 From: ikedam Date: Thu, 12 Dec 2013 00:10:32 +0900 Subject: [PATCH 02/36] [JENKINS-19976] Tests for the behavior of loading a depended plugin dynamically. --- .../test/java/hudson/PluginManagerTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 6add26b107..ed0b50565d 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -41,6 +41,7 @@ import org.jvnet.hudson.test.recipes.WithPlugin; import org.jvnet.hudson.test.recipes.WithPluginManager; import java.io.File; +import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; @@ -222,4 +223,104 @@ public class PluginManagerTest extends HudsonTestCase { // TODO required plugin installed but inactive } + // plugin "depender" optionally depends on plugin "dependee". + // they are written like this: + // dummy.comp.depender.Depender: + // public class Depender { + // public static String getValue() { + // if (Jenkins.getInstance().getPlugin("dependee") != null) { + // return Dependee.getValue(); + // } + // return "depender"; + // } + // } + // + // dummy.comp.dependee.Dependee: + // public class Dependee { + // public static String getValue() { + // return "dependee"; + // } + // } + + + /** + * call dummy.comp.depender.Depender.getValue(). + * + * @return + * @throws Exception + */ + private String callDependerValue() throws Exception { + Class c = jenkins.getPluginManager().uberClassLoader.loadClass("dummy.comp.depender.Depender"); + Method m = c.getMethod("getValue"); + return (String)m.invoke(null); + } + + /** + * Load "dependee" and then load "depender". + * Asserts that "depender" can access to "dependee". + * + * @throws Exception + */ + public void testInstallDependingPluginWithoutRestart() throws Exception { + // Load dependee. + { + String target = "dependee.hpi"; + URL src = getClass().getClassLoader().getResource(String.format("plugins/%s", target)); + File dest = new File(jenkins.getRootDir(), String.format("plugins/%s", target)); + FileUtils.copyURLToFile(src, dest); + jenkins.pluginManager.dynamicLoad(dest); + } + + // before load depender, failed to call Depender.getValue() + try { + callDependerValue(); + fail(); + } catch( Exception e ) { + } + + // Load depender. + { + String target = "depender.hpi"; + URL src = getClass().getClassLoader().getResource(String.format("plugins/%s", target)); + File dest = new File(jenkins.getRootDir(), String.format("plugins/%s", target)); + FileUtils.copyURLToFile(src, dest); + jenkins.pluginManager.dynamicLoad(dest); + } + + // depender successfully access to dependee. + assertEquals("dependee", callDependerValue()); + } + + /** + * Load "depender" and then load "dependee". + * Asserts that "depender" can access to "dependee". + * + * @throws Exception + */ + public void testInstallDependedPluginWithoutRestart() throws Exception { + // Load depender. + { + String target = "depender.hpi"; + URL src = getClass().getClassLoader().getResource(String.format("plugins/%s", target)); + File dest = new File(jenkins.getRootDir(), String.format("plugins/%s", target)); + FileUtils.copyURLToFile(src, dest); + jenkins.pluginManager.dynamicLoad(dest); + } + + // before load dependee, depender does not access to dependee. + assertEquals("depender", callDependerValue()); + + // Load dependee. + { + String target = "dependee.hpi"; + URL src = getClass().getClassLoader().getResource(String.format("plugins/%s", target)); + File dest = new File(jenkins.getRootDir(), String.format("plugins/%s", target)); + FileUtils.copyURLToFile(src, dest); + jenkins.pluginManager.dynamicLoad(dest); + } + + // depender successfully access to dependee. + assertEquals("dependee", callDependerValue()); + } + } From 536bac6425b465ad98650083ba81655265e5508f Mon Sep 17 00:00:00 2001 From: ikedam Date: Thu, 12 Dec 2013 22:17:42 +0900 Subject: [PATCH 03/36] [JENKINS-19976] Added tests how dynamic load works for @Extension. --- .../test/java/hudson/PluginManagerTest.java | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index ed0b50565d..62a0f9a264 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -225,7 +225,17 @@ public class PluginManagerTest extends HudsonTestCase { // plugin "depender" optionally depends on plugin "dependee". // they are written like this: - // dummy.comp.depender.Depender: + // org.jenkinsci.plugins.dependencytest.dependee: + // public class Dependee { + // public static String getValue() { + // return "dependee"; + // } + // } + // + // public abstract class DependeeExtensionPoint implements ExtensionPoint { + // } + // + // org.jenkinsci.plugins.dependencytest.depender: // public class Depender { // public static String getValue() { // if (Jenkins.getInstance().getPlugin("dependee") != null) { @@ -234,23 +244,20 @@ public class PluginManagerTest extends HudsonTestCase { // return "depender"; // } // } - // - // dummy.comp.dependee.Dependee: - // public class Dependee { - // public static String getValue() { - // return "dependee"; - // } + // + // @Extension(optional=true) + // public class DependerExtension extends DependeeExtensionPoint { // } /** - * call dummy.comp.depender.Depender.getValue(). + * call org.jenkinsci.plugins.dependencytest.depender.Depender.getValue(). * * @return * @throws Exception */ private String callDependerValue() throws Exception { - Class c = jenkins.getPluginManager().uberClassLoader.loadClass("dummy.comp.depender.Depender"); + Class c = jenkins.getPluginManager().uberClassLoader.loadClass("org.jenkinsci.plugins.dependencytest.depender.Depender"); Method m = c.getMethod("getValue"); return (String)m.invoke(null); } @@ -271,13 +278,16 @@ public class PluginManagerTest extends HudsonTestCase { jenkins.pluginManager.dynamicLoad(dest); } - // before load depender, failed to call Depender.getValue() + // before load depender, of course failed to call Depender.getValue() try { callDependerValue(); fail(); - } catch( Exception e ) { + } catch (ClassNotFoundException _) { } + // No extensions exist. + assertTrue(jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint").isEmpty()); + // Load depender. { String target = "depender.hpi"; @@ -287,8 +297,12 @@ public class PluginManagerTest extends HudsonTestCase { jenkins.pluginManager.dynamicLoad(dest); } - // depender successfully access to dependee. + // (MUST) Not throws an exception + // (SHOULD) depender successfully accesses to dependee. assertEquals("dependee", callDependerValue()); + + // Extension in depender is loaded. + assertFalse(jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint").isEmpty()); } /** @@ -310,6 +324,13 @@ public class PluginManagerTest extends HudsonTestCase { // before load dependee, depender does not access to dependee. assertEquals("depender", callDependerValue()); + // before load dependee, of course failed to list extensions for dependee. + try { + jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint"); + fail(); + } catch( ClassNotFoundException _ ){ + } + // Load dependee. { String target = "dependee.hpi"; @@ -321,6 +342,10 @@ public class PluginManagerTest extends HudsonTestCase { // depender successfully access to dependee. assertEquals("dependee", callDependerValue()); + + // No extensions exist. + // extensions in depender is not loaded. + assertTrue(jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint").isEmpty()); } } From 2c047e0264a03465a3c017abf86c035a011c2479 Mon Sep 17 00:00:00 2001 From: ikedam Date: Thu, 12 Dec 2013 22:24:50 +0900 Subject: [PATCH 04/36] [JENKINS-19976] Added plugins to test dynamic loading. --- test/src/test/resources/plugins/dependee.hpi | Bin 0 -> 4672 bytes test/src/test/resources/plugins/depender.hpi | Bin 0 -> 5505 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/src/test/resources/plugins/dependee.hpi create mode 100644 test/src/test/resources/plugins/depender.hpi diff --git a/test/src/test/resources/plugins/dependee.hpi b/test/src/test/resources/plugins/dependee.hpi new file mode 100644 index 0000000000000000000000000000000000000000..0459ae63727d9427b4fe44137ef88d90413c3338 GIT binary patch literal 4672 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 literal 0 HcmV?d00001 diff --git a/test/src/test/resources/plugins/depender.hpi b/test/src/test/resources/plugins/depender.hpi new file mode 100644 index 0000000000000000000000000000000000000000..bebd4b6943a835bdf14eeaae5cb0768862779a37 GIT binary patch literal 5505 zcmb_gdpy(o8y{LAx7x06zGirEkJA|#u#%`lr0u+X&M~23tfc}jhuka z2+cq(cP6b|zniFB-0PV-Ztp=R;@ag0(zEBg?~%yaFYE*-(3hv=rEd+c`VcqzkV?>$ z(``aH-%m?Wy!whJyl0I zLM#hTcZ!(`%|e2XDyo%om@adC>|vBEm6ggN6DTpFBl#p`u3kM|vPnINvf-Jhf`t05 zjGk1l8^Jba`|VU_BcH3{ z!rO#W866CNO4{VhKHW=AFC}t>Zl0S7^$Q6{Pbw<9$xYRU>L?4&SCC?5Ft?gX_fliV z@?zY=TZQO0Tx@D?z=jSe3=NIn#I z3c-i8lw>d>=(nNk>EpL|30>EGhhsmUNN;(hTX zPdo`nrQpewB_Iuc7V@hEKL$4U=gbt?H-`t+)CV4EHrNC}t3ddEJdsE(Ft1lB=;y7k zQ?cJzr)1gEXj`YYvA(ed-B^DEYi6!$)`V8FuWPZj4!dn@oA~xV@ohyigdUt#Pal33{+j%bnmt>qzTmQiKdxmppY<6 zwNFA5+|XCL(ZD7v`vEJ zY9D5Y>PEI*#%}K1yYb10KxCgWO@#m0DQur+86kSO&6_Knh8Zq0u8Pe#MZTTnQlI4h zCy!i8VMOs+olLmG+5WIJ0;Xb|R&*gOwj z;-h^)2a!+BU83Rjq_nfsLo2-%+hk)Y*?rY^Ym_dx+0L0}v?^C`X^}@@y$qvr^&JSC z1jQ!e0_Lx~Qr_`Bi!wGePFaH})5;GPJ@6*x@WhA)4L41USH-)D&3WbsazG{IU0XLb zohdw?b9ydpP03_>w?9Uz2fUm*s zYxjTiH6v}mI6c#9D;jDnda>zgjl5LsddKKk=Rn6z+Y!#N6^Wt&7fKYU|Gd%ud;G*F zl}#0uvM)}H^jtc@ZNI*uxw(|eUoasHcw8{A)kxBdLyx0FZ2zCt16HDsN)ACI9-WVk zMV43*;RUZAXA}k{)n&97#$&kjGF@7t#0)oFdp!1>B8uVfSy^(t(kUS}JRU~dyGzH7@zA58ml=- ztvVpC-_YAaE50;%5A*2F!M~6&yNLFX4!hwwg$fms#)FlK3ez-G9;zB)!|ykFI!q!W zxNgp>S+^j!b1(C%@m=m;(eae??Bk99?g>>@X?QN`gCfleW~F@VjWmn&_nCkfoB(eu z?0iH54k&|oGCa_mcsDLj@EC&M7ZJi`6At&YR-d$dcG3QiLzyNgY?MY+{Tf9t!k*_ho%#_GBM{_15RzNSwWwQ>vq1D|G~dD(X$+yy;!PW_*GsU`Z&BZR~2TKhfncGxmV1 z`WPz$cvgtMH}6gL`do2Kr?L8o^L$Uetxa#r)q8^zxz~7ifs_3M!QEiL96)a zN1p7eaq7OfJdJEcNs{r*rxhpLMmi3w`Hh;p8jrR5te4mF-`R(&t(~eH>S%Cv30%gD zNaeNeYdSV1c|is>?jC2d@~s{&>O552nfC8r9J@vu`EzJ_Xu02VXyj4sP-1-98%M5x zIy$Jbc~vp!S6R~$yj_T*s4yMkjjn>KyRVGGq-(bxkV9M;9Vu)Tkh%FDbu|n35P!MA z1Xq3~$nd20nH!;38z1+fekF*ta zr1DennlN2{ehrtnIB%3ti@u*G z9AU_qG5F26G?R_}5(pB5CnHo5cr(}*tb|O2#bpCrfxaA2uosY>^nqw8E^sAca>Xy; zqAwmxaf2%ole?S^H{%vd!GJ3h69(+cbk*rfFklvr%S%Cmjf4rQ3k-FvkY5(6?>iD) zsh9{}-$7uGgiC1yu2@X0YyXKwA1{}}1lt4?^Yss7E@~RzHv!l-m?8|I?2X7Y0bOkt z5eu3KT^9S5j4mxBG?|U81q&L}F6_50CJ;B!p;$f7Hkep+ZqCE@FfIE1L+bWY$TTDLHfb;#>WkWR|&@RRz}ZkEwzN@CO>*cMFF> zCc@$>2u@mG4p;?wff~*5Hhu1*$KYb-Z&ONVG4qekVc!?=ZAwM|tO&L##cndATO%WM x=$B=Eozs}+|B`&HO`a)IP+*byf6Ri6>wVkuti#<{{u>rL(~8O literal 0 HcmV?d00001 From 38f670cf39224c40752e47e279710afeebc585e7 Mon Sep 17 00:00:00 2001 From: ikedam Date: Fri, 13 Dec 2013 23:00:06 +0900 Subject: [PATCH 05/36] [JENKINS-19976] Fixed confusing comments. --- test/src/test/java/hudson/PluginManagerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 62a0f9a264..b068598c94 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -297,8 +297,7 @@ public class PluginManagerTest extends HudsonTestCase { jenkins.pluginManager.dynamicLoad(dest); } - // (MUST) Not throws an exception - // (SHOULD) depender successfully accesses to dependee. + // depender successfully accesses to dependee. assertEquals("dependee", callDependerValue()); // Extension in depender is loaded. @@ -340,7 +339,8 @@ public class PluginManagerTest extends HudsonTestCase { jenkins.pluginManager.dynamicLoad(dest); } - // depender successfully access to dependee. + // (MUST) Not throws an exception + // (SHOULD) depender successfully accesses to dependee. assertEquals("dependee", callDependerValue()); // No extensions exist. From ad0144f51f3bf5c104ce12bc3235bee807db9e75 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 17 Mar 2014 11:08:16 -0700 Subject: [PATCH 06/36] making the version more machine discoverable --- core/src/main/resources/hudson/AboutJenkins/index.jelly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/AboutJenkins/index.jelly b/core/src/main/resources/hudson/AboutJenkins/index.jelly index 8b43cd979f..259b52e9f1 100644 --- a/core/src/main/resources/hudson/AboutJenkins/index.jelly +++ b/core/src/main/resources/hudson/AboutJenkins/index.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. -

${%about(app.VERSION)}

+

${%about(app.VERSION)}

${%blurb}

From 5de6a8d06dbc2fe7e34bf8fb531c6588bbc77db8 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Mon, 17 Mar 2014 22:30:58 +0400 Subject: [PATCH 07/36] [JENKINS-21190] - lib/layout/layout.jelly: Interpret norefresh=="false" as "autorefresh is enabled" The fix allows to explicitly disable automatic page refreshes in the @View extensions. Resolves https://issues.jenkins-ci.org/browse/JENKINS-21190 Signed-off-by: Oleg Nenashev --- core/src/main/java/hudson/model/View.java | 9 +++++++++ core/src/main/resources/hudson/model/View/index.jelly | 2 +- core/src/main/resources/lib/layout/breadcrumbBar.jelly | 2 +- core/src/main/resources/lib/layout/layout.jelly | 5 ++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 0a418e1310..fee18dd273 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -373,6 +373,15 @@ public abstract class View extends AbstractModelObject implements AccessControll return true; } + /** + * Enables or disables automatic refreshes of the view. + * By default, automatic refreshes are enabled. + * @since TODO: define a version + */ + public boolean isAutomaticRefreshEnabled() { + return true; + } + /** * If true, only show relevant executors */ diff --git a/core/src/main/resources/hudson/model/View/index.jelly b/core/src/main/resources/hudson/model/View/index.jelly index 464262f320..b74f00dc4d 100644 --- a/core/src/main/resources/hudson/model/View/index.jelly +++ b/core/src/main/resources/hudson/model/View/index.jelly @@ -24,7 +24,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/lib/layout/breadcrumbBar.jelly b/core/src/main/resources/lib/layout/breadcrumbBar.jelly index f00fb45e99..ef1b411328 100644 --- a/core/src/main/resources/lib/layout/breadcrumbBar.jelly +++ b/core/src/main/resources/lib/layout/breadcrumbBar.jelly @@ -40,7 +40,7 @@ THE SOFTWARE.
- +
diff --git a/core/src/main/resources/lib/layout/layout.jelly b/core/src/main/resources/lib/layout/layout.jelly index 95d73c8d01..46acef6e2b 100644 --- a/core/src/main/resources/lib/layout/layout.jelly +++ b/core/src/main/resources/lib/layout/layout.jelly @@ -34,7 +34,7 @@ THE SOFTWARE. Title of the HTML page. Rendered into <title> tag. - If non-null, auto refresh is disabled on this page. + If non-null and not "false", auto refresh is disabled on this page. This is necessary for pages that include forms. @@ -67,8 +67,7 @@ ${h.initPageVariables(context)} which I suspect can end up creating sessions for wrong resource types (such as static resources.) --> - - + ${h.advertiseHeaders(response)} From 4bb0f98901e2836688ae6c56b9f112c105bc1c54 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 17 Mar 2014 17:54:48 -0400 Subject: [PATCH 08/36] [FIXED JENKINS-21190] [FIXED JENKINS-21191] Noting merge of #1082. --- changelog.html | 4 ++++ core/src/main/java/hudson/model/View.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.html b/changelog.html index 6b4c00901e..739732fe96 100644 --- a/changelog.html +++ b/changelog.html @@ -55,6 +55,10 @@ Upcoming changes