Skip to content

Commit

Permalink
Merge branch 'master' of github.com:jenkinsci/jenkins into JENKINS-28245
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarolobato committed Nov 28, 2016
2 parents e632f68 + a05d942 commit 7f11459
Show file tree
Hide file tree
Showing 12 changed files with 329 additions and 45 deletions.
22 changes: 22 additions & 0 deletions changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@
<li class=>
</ul>
</div><!--=TRUNK-END=-->
<h3><a name=v2.34>What's new in 2.34</a> (2016/11/27)</h3>
<ul class=image>
<li class="rfe">
Improve performance of <code>Action</code> retrieval methods.
It speeds up core and plugin logic operating with <code>Actionable</code> objects like items, folders, nodes, etc.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-38867">issue 38867</a>)
<li class="rfe">
Update the <a href="https://github.com/jenkinsci/sshd-module">SSHD module</a> from <code>1.7</code> to <code>1.8</code>.
The change disables obsolete Ciphers: AES128CBC, TripleDESCBC, and BlowfishCBC.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-39805">issue 39805</a>)
<li class="rfe">
Update the <a href="https://github.com/kohsuke/winp">Windows process management library (WinP)</a> from <code>1.22</code> to <code>1.24</code>.
Full changelog is available <a href="https://github.com/kohsuke/winp/blob/master/CHANGELOG.md">here</a>, only major issues are mentioned below.
(<a href="https://github.com/jenkinsci/jenkins/pull/2619">pull 2619</a>)
<li class="bug">
WinP 1.24: Native class now tries loading DLLs from the temporary location.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20913">issue 20913</a>)
<li class="bug">
WinP 1.24: WinP sometimes kills wrong processes when using <code>killRecursive</code>.
It was likely impacting process termination on Windows agents.
(<a href="https://github.com/kohsuke/winp/issues/22">WinP Issue #22</a>)
</ul>
<h3><a name=v2.33>What's new in 2.33</a> (2016/11/20)</h3>
<ul class=image>
<li class="rfe">
Expand Down
2 changes: 1 addition & 1 deletion cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.34-SNAPSHOT</version>
<version>2.35-SNAPSHOT</version>
</parent>

<artifactId>cli</artifactId>
Expand Down
4 changes: 2 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.34-SNAPSHOT</version>
<version>2.35-SNAPSHOT</version>
</parent>

<artifactId>jenkins-core</artifactId>
Expand Down Expand Up @@ -496,7 +496,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jvnet.winp</groupId>
<artifactId>winp</artifactId>
<version>1.22</version>
<version>1.24</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci</groupId>
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/hudson/model/AbstractItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,14 @@ public void movedTo(DirectlyModifiableTopLevelItemGroup destination, AbstractIte
*/
public abstract Collection<? extends Job> getAllJobs();

@Exported
public final String getFullName() {
String n = getParent().getFullName();
if(n.length()==0) return getName();
else return n+'/'+getName();
}

@Exported
public final String getFullDisplayName() {
String n = getParent().getFullDisplayName();
if(n.length()==0) return getDisplayName();
Expand Down
63 changes: 46 additions & 17 deletions core/src/main/java/hudson/model/Actionable.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
package hudson.model;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.ExtensionList;
import hudson.Util;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -33,7 +32,6 @@
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import jenkins.model.ModelObjectWithContextMenu;
Expand Down Expand Up @@ -68,13 +66,13 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
* This method by default returns only <em>persistent</em> actions
* (though some subclasses override it to return an extended unmodifiable list).
*
* @return
* may be empty but never null.
* @return a possibly empty list
* @deprecated Normally outside code should not call this method any more.
* Use {@link #getAllActions}, or {@link #addAction}, or {@link #replaceAction}.
* May still be called for compatibility reasons from subclasses predating {@link TransientActionFactory}.
*/
@Deprecated
@Nonnull
public List<Action> getActions() {
synchronized (this) {
if(actions == null) {
Expand All @@ -91,33 +89,53 @@ public List<Action> getActions() {
* @since 1.548
*/
@Exported(name="actions")
@Nonnull
public final List<? extends Action> getAllActions() {
List<Action> _actions = new ArrayList<Action>(getActions());
for (TransientActionFactory<?> taf : ExtensionList.lookup(TransientActionFactory.class)) {
if (taf.type().isInstance(this)) {
try {
_actions.addAll(createFor(taf));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Could not load actions from " + taf + " for " + this, e);
List<Action> _actions = getActions();
boolean adding = false;
for (TransientActionFactory<?> taf : TransientActionFactory.factoriesFor(getClass(), Action.class)) {
Collection<? extends Action> additions = createFor(taf);
if (!additions.isEmpty()) {
if (!adding) { // need to make a copy
adding = true;
_actions = new ArrayList<>(_actions);
}
_actions.addAll(additions);
}
}
return Collections.unmodifiableList(_actions);
}

private <T> Collection<? extends Action> createFor(TransientActionFactory<T> taf) {
return taf.createFor(taf.type().cast(this));
try {
Collection<? extends Action> result = taf.createFor(taf.type().cast(this));
for (Action a : result) {
if (!taf.actionType().isInstance(a)) {
LOGGER.log(Level.WARNING, "Actions from {0} for {1} included {2} not assignable to {3}", new Object[] {taf, this, a, taf.actionType()});
return Collections.emptySet();
}
}
return result;
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Could not load actions from " + taf + " for " + this, e);
return Collections.emptySet();
}
}

/**
* Gets all actions of a specified type that contributed to this object.
*
* @param type The type of action to return.
* @return
* may be empty but never null.
* @return an unmodifiable, possible empty list
* @see #getAction(Class)
*/
@Nonnull
public <T extends Action> List<T> getActions(Class<T> type) {
return Util.filter(getAllActions(), type);
List<T> _actions = Util.filter(getActions(), type);
for (TransientActionFactory<?> taf : TransientActionFactory.factoriesFor(getClass(), type)) {
_actions.addAll(Util.filter(createFor(taf), type));
}
return Collections.unmodifiableList(_actions);
}

/**
Expand Down Expand Up @@ -305,9 +323,20 @@ public Action getAction(int index) {
* @see #getActions(Class)
*/
public <T extends Action> T getAction(Class<T> type) {
for (Action a : getAllActions())
if (type.isInstance(a))
// Shortcut: if the persisted list has one, return it.
for (Action a : getActions()) {
if (type.isInstance(a)) {
return type.cast(a);
}
}
// Otherwise check transient factories.
for (TransientActionFactory<?> taf : TransientActionFactory.factoriesFor(getClass(), type)) {
for (Action a : createFor(taf)) {
if (type.isInstance(a)) {
return type.cast(a);
}
}
}
return null;
}

Expand Down
76 changes: 75 additions & 1 deletion core/src/main/java/jenkins/model/TransientActionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,22 @@

package jenkins.model;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import hudson.ExtensionList;
import hudson.ExtensionListListener;
import hudson.ExtensionPoint;
import hudson.model.Action;
import hudson.model.Actionable;
import hudson.model.TopLevelItem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* Allows you to add actions to any kind of object at once.
Expand All @@ -48,12 +58,76 @@ public abstract class TransientActionFactory<T> implements ExtensionPoint {
*/
public abstract Class<T> type();

/**
* A supertype of any actions this factory might produce.
* Defined so that factories which produce irrelevant actions need not be consulted by, e.g., {@link Actionable#getAction(Class)}.
* For historical reasons this defaults to {@link Action} itself.
* If your implementation was returning multiple disparate kinds of actions, it is best to split it into two factories.
* <p>If an API defines a abstract {@link Action} subtype and you are providing a concrete implementation,
* you may return the API type here to delay class loading.
* @return a bound for the result of {@link #createFor}
*/
public /* abstract */ Class<? extends Action> actionType() {
return Action.class;
}

/**
* Creates actions for a given object.
* This may be called frequently for the same object, so if your implementation is expensive, do your own caching.
* @param target an actionable object
* @return a possible empty set of actions
* @return a possible empty set of actions (typically either using {@link Collections#emptySet} or {@link Collections#singleton})
*/
public abstract @Nonnull Collection<? extends Action> createFor(@Nonnull T target);

/** @see <a href="http://stackoverflow.com/a/24336841/12916">no pairs/tuples in Java</a> */
private static class CacheKey {
private final Class<?> type;
private final Class<? extends Action> actionType;
CacheKey(Class<?> type, Class<? extends Action> actionType) {
this.type = type;
this.actionType = actionType;
}
@Override
public boolean equals(Object obj) {
return obj instanceof CacheKey && type == ((CacheKey) obj).type && actionType == ((CacheKey) obj).actionType;
}
@Override
public int hashCode() {
return type.hashCode() ^ actionType.hashCode();
}
}
@SuppressWarnings("rawtypes")
private static final LoadingCache<ExtensionList<TransientActionFactory>, LoadingCache<CacheKey, List<TransientActionFactory<?>>>> cache =
CacheBuilder.newBuilder().weakKeys().build(new CacheLoader<ExtensionList<TransientActionFactory>, LoadingCache<CacheKey, List<TransientActionFactory<?>>>>() {
@Override
public LoadingCache<CacheKey, List<TransientActionFactory<?>>> load(final ExtensionList<TransientActionFactory> allFactories) throws Exception {
final LoadingCache<CacheKey, List<TransientActionFactory<?>>> perJenkinsCache =
CacheBuilder.newBuilder().build(new CacheLoader<CacheKey, List<TransientActionFactory<?>>>() {
@Override
public List<TransientActionFactory<?>> load(CacheKey key) throws Exception {
List<TransientActionFactory<?>> factories = new ArrayList<>();
for (TransientActionFactory<?> taf : allFactories) {
Class<? extends Action> actionType = taf.actionType();
if (taf.type().isAssignableFrom(key.type) && (key.actionType.isAssignableFrom(actionType) || actionType.isAssignableFrom(key.actionType))) {
factories.add(taf);
}
}
return factories;
}
});
allFactories.addListener(new ExtensionListListener() {
@Override
public void onChange() {
perJenkinsCache.invalidateAll();
}
});
return perJenkinsCache;
}
});

@Restricted(NoExternalUse.class) // pending a need for it outside Actionable
public static Iterable<? extends TransientActionFactory<?>> factoriesFor(Class<?> type, Class<? extends Action> actionType) {
return cache.getUnchecked(ExtensionList.lookup(TransientActionFactory.class)).getUnchecked(new CacheKey(type, actionType));
}

}
Loading

0 comments on commit 7f11459

Please sign in to comment.