提交 5ab06c4a 编写于 作者: K Kohsuke Kawaguchi

Merge remote-tracking branch 'origin/master' into lazy-load

...@@ -55,6 +55,12 @@ Upcoming changes</a> ...@@ -55,6 +55,12 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. --> <!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=--> <div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image> <ul class=image>
<li class=bug>
Log recorders do not work reliably.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-15226">issue 15226</a>)
<li class=bug>
<code>FilePath.validateAntFileMask</code> too slow for <code>/configure</code>.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-7214">issue 7214</a>)
<li class=> <li class=>
</ul> </ul>
</div><!--=TRUNK-END=--> </div><!--=TRUNK-END=-->
......
...@@ -414,7 +414,7 @@ public abstract class ExtensionFinder implements ExtensionPoint { ...@@ -414,7 +414,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* Instead, we should just drop the failing plugins. * Instead, we should just drop the failing plugins.
*/ */
public static final Scope FAULT_TOLERANT_SCOPE = new Scope() { public static final Scope FAULT_TOLERANT_SCOPE = new Scope() {
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) { public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
final Provider<T> base = Scopes.SINGLETON.scope(key,unscoped); final Provider<T> base = Scopes.SINGLETON.scope(key,unscoped);
return new Provider<T>() { return new Provider<T>() {
public T get() { public T get() {
...@@ -423,6 +423,9 @@ public abstract class ExtensionFinder implements ExtensionPoint { ...@@ -423,6 +423,9 @@ public abstract class ExtensionFinder implements ExtensionPoint {
} catch (Exception e) { } catch (Exception e) {
LOGGER.log(Level.WARNING,"Failed to instantiate. Skipping this component",e); LOGGER.log(Level.WARNING,"Failed to instantiate. Skipping this component",e);
return null; return null;
} catch (LinkageError e) {
LOGGER.log(Level.WARNING,"Failed to instantiate. Skipping this component",e);
return null;
} }
} }
}; };
......
...@@ -27,7 +27,6 @@ package hudson; ...@@ -27,7 +27,6 @@ package hudson;
import hudson.Launcher.LocalLauncher; import hudson.Launcher.LocalLauncher;
import hudson.Launcher.RemoteLauncher; import hudson.Launcher.RemoteLauncher;
import hudson.model.AbstractDescribableImpl;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import hudson.model.TaskListener; import hudson.model.TaskListener;
import hudson.model.AbstractProject; import hudson.model.AbstractProject;
...@@ -840,7 +839,7 @@ public final class FilePath implements Serializable { ...@@ -840,7 +839,7 @@ public final class FilePath implements Serializable {
return channel.call(wrapper); return channel.call(wrapper);
} catch (TunneledInterruptedException e) { } catch (TunneledInterruptedException e) {
throw (InterruptedException)new InterruptedException().initCause(e); throw (InterruptedException)new InterruptedException(e.getMessage()).initCause(e);
} catch (AbortException e) { } catch (AbortException e) {
throw e; // pass through so that the caller can catch it as AbortException throw e; // pass through so that the caller can catch it as AbortException
} catch (IOException e) { } catch (IOException e) {
...@@ -1976,8 +1975,26 @@ public final class FilePath implements Serializable { ...@@ -1976,8 +1975,26 @@ public final class FilePath implements Serializable {
* @see #validateFileMask(FilePath, String) * @see #validateFileMask(FilePath, String)
*/ */
public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException { public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException {
return validateAntFileMask(fileMasks, Integer.MAX_VALUE);
}
/**
* Like {@link #validateAntFileMask(String)} but performing only a bounded number of operations.
* <p>Whereas the unbounded overload is appropriate for calling from cancelable, long-running tasks such as build steps,
* this overload should be used when an answer is needed quickly, such as for {@link #validateFileMask(String)}
* or anything else returning {@link FormValidation}.
* <p>If a positive match is found, {@code null} is returned immediately.
* A message is returned in case the file pattern can definitely be determined to not match anything in the directory within the alloted time.
* If the time runs out without finding a match but without ruling out the possibility that there might be one, {@link InterruptedException} is thrown,
* in which case the calling code should give the user the benefit of the doubt and use {@link hudson.util.FormValidation.Kind#OK} (with or without a message).
* @param bound a maximum number of negative operations (deliberately left vague) to perform before giving up on a precise answer; 10_000 is a reasonable pick
* @throws InterruptedException not only in case of a channel failure, but also if too many operations were performed without finding any matches
* @since 1.484
*/
public String validateAntFileMask(final String fileMasks, final int bound) throws IOException, InterruptedException {
return act(new FileCallable<String>() { return act(new FileCallable<String>() {
public String invoke(File dir, VirtualChannel channel) throws IOException { private static final long serialVersionUID = 1;
public String invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException {
if(fileMasks.startsWith("~")) if(fileMasks.startsWith("~"))
return Messages.FilePath_TildaDoesntWork(); return Messages.FilePath_TildaDoesntWork();
...@@ -2074,10 +2091,28 @@ public final class FilePath implements Serializable { ...@@ -2074,10 +2091,28 @@ public final class FilePath implements Serializable {
return null; // no error return null; // no error
} }
private boolean hasMatch(File dir, String pattern) { private boolean hasMatch(File dir, String pattern) throws InterruptedException {
FileSet fs = Util.createFileSet(dir,pattern); class Cancel extends RuntimeException {}
DirectoryScanner ds = fs.getDirectoryScanner(new Project()); DirectoryScanner ds = bound == Integer.MAX_VALUE ? new DirectoryScanner() : new DirectoryScanner() {
int ticks;
@Override public synchronized boolean isCaseSensitive() {
if (!filesIncluded.isEmpty() || !dirsIncluded.isEmpty() || ticks++ > bound) {
throw new Cancel();
}
return super.isCaseSensitive();
}
};
ds.setBasedir(dir);
ds.setIncludes(new String[] {pattern});
try {
ds.scan();
} catch (Cancel c) {
if (ds.getIncludedFilesCount()!=0 || ds.getIncludedDirsCount()!=0) {
return true;
} else {
throw new InterruptedException("no matches found within " + bound);
}
}
return ds.getIncludedFilesCount()!=0 || ds.getIncludedDirsCount()!=0; return ds.getIncludedFilesCount()!=0 || ds.getIncludedDirsCount()!=0;
} }
...@@ -2126,11 +2161,11 @@ public final class FilePath implements Serializable { ...@@ -2126,11 +2161,11 @@ public final class FilePath implements Serializable {
if(!exists()) // no workspace. can't check if(!exists()) // no workspace. can't check
return FormValidation.ok(); return FormValidation.ok();
String msg = validateAntFileMask(value); String msg = validateAntFileMask(value, 10000);
if(errorIfNotExist) return FormValidation.error(msg); if(errorIfNotExist) return FormValidation.error(msg);
else return FormValidation.warning(msg); else return FormValidation.warning(msg);
} catch (InterruptedException e) { } catch (InterruptedException e) {
return FormValidation.ok(); return FormValidation.ok(Messages.FilePath_did_not_manage_to_validate_may_be_too_sl(value));
} }
} }
......
...@@ -87,6 +87,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable { ...@@ -87,6 +87,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
public static final class Target { public static final class Target {
public final String name; public final String name;
private final int level; private final int level;
private transient /* almost final*/ Logger logger;
public Target(String name, Level level) { public Target(String name, Level level) {
this(name,level.intValue()); this(name,level.intValue());
...@@ -118,7 +119,10 @@ public class LogRecorder extends AbstractModelObject implements Saveable { ...@@ -118,7 +119,10 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
} }
public Logger getLogger() { public Logger getLogger() {
return Logger.getLogger(name); if (logger == null) {
logger = Logger.getLogger(name);
}
return logger;
} }
/** /**
......
...@@ -30,6 +30,7 @@ import hudson.triggers.Trigger; ...@@ -30,6 +30,7 @@ import hudson.triggers.Trigger;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import java.util.Random; import java.util.Random;
import java.util.Timer;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -84,7 +85,10 @@ public abstract class AperiodicWork extends SafeTimerTask implements ExtensionPo ...@@ -84,7 +85,10 @@ public abstract class AperiodicWork extends SafeTimerTask implements ExtensionPo
@Override @Override
public final void doRun() throws Exception{ public final void doRun() throws Exception{
doAperiodicRun(); doAperiodicRun();
Trigger.timer.schedule(getNewInstance(), getRecurrencePeriod()); Timer timer = Trigger.timer;
if (timer != null) {
timer.schedule(getNewInstance(), getRecurrencePeriod());
}
} }
protected abstract void doAperiodicRun(); protected abstract void doAperiodicRun();
......
...@@ -85,6 +85,7 @@ import java.util.List; ...@@ -85,6 +85,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.Timer;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
...@@ -93,7 +94,6 @@ import java.util.concurrent.atomic.AtomicLong; ...@@ -93,7 +94,6 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.management.timer.Timer;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
...@@ -1746,8 +1746,11 @@ public class Queue extends ResourceController implements Saveable { ...@@ -1746,8 +1746,11 @@ public class Queue extends ResourceController implements Saveable {
MaintainTask(Queue queue) { MaintainTask(Queue queue) {
this.queue = new WeakReference<Queue>(queue); this.queue = new WeakReference<Queue>(queue);
long interval = 5 * Timer.ONE_SECOND; long interval = 5000;
Trigger.timer.schedule(this, interval, interval); Timer timer = Trigger.timer;
if (timer != null) {
timer.schedule(this, interval, interval);
}
} }
protected void doRun() { protected void doRun() {
......
...@@ -32,12 +32,8 @@ import hudson.Extension; ...@@ -32,12 +32,8 @@ import hudson.Extension;
import hudson.ExtensionPoint; import hudson.ExtensionPoint;
import hudson.Indenter; import hudson.Indenter;
import hudson.Util; import hudson.Util;
import hudson.XmlFile;
import hudson.model.Descriptor.FormException; import hudson.model.Descriptor.FormException;
import hudson.model.Node.Mode;
import hudson.model.labels.LabelAtom;
import hudson.model.labels.LabelAtomPropertyDescriptor; import hudson.model.labels.LabelAtomPropertyDescriptor;
import hudson.model.listeners.SaveableListener;
import hudson.scm.ChangeLogSet.Entry; import hudson.scm.ChangeLogSet.Entry;
import hudson.search.CollectionSearchIndex; import hudson.search.CollectionSearchIndex;
import hudson.search.SearchIndexBuilder; import hudson.search.SearchIndexBuilder;
...@@ -48,11 +44,9 @@ import hudson.security.PermissionGroup; ...@@ -48,11 +44,9 @@ import hudson.security.PermissionGroup;
import hudson.security.PermissionScope; import hudson.security.PermissionScope;
import hudson.util.AlternativeUiTextProvider; import hudson.util.AlternativeUiTextProvider;
import hudson.util.AlternativeUiTextProvider.Message; import hudson.util.AlternativeUiTextProvider.Message;
import hudson.util.AtomicFileWriter;
import hudson.util.DescribableList; import hudson.util.DescribableList;
import hudson.util.DescriptorList; import hudson.util.DescriptorList;
import hudson.util.IOException2; import hudson.util.IOException2;
import hudson.util.IOUtils;
import hudson.util.RunList; import hudson.util.RunList;
import hudson.util.XStream2; import hudson.util.XStream2;
import hudson.views.ListViewColumn; import hudson.views.ListViewColumn;
...@@ -77,7 +71,6 @@ import javax.xml.transform.stream.StreamResult; ...@@ -77,7 +71,6 @@ import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringWriter; import java.io.StringWriter;
...@@ -92,7 +85,6 @@ import java.util.HashMap; ...@@ -92,7 +85,6 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -645,7 +637,7 @@ public abstract class View extends AbstractModelObject implements AccessControll ...@@ -645,7 +637,7 @@ public abstract class View extends AbstractModelObject implements AccessControll
@Exported @Exported
public final List<UserInfo> users; public final List<UserInfo> users;
public final Object parent; public final ModelObject parent;
public People(Jenkins parent) { public People(Jenkins parent) {
this.parent = parent; this.parent = parent;
......
...@@ -36,6 +36,7 @@ import java.io.IOException; ...@@ -36,6 +36,7 @@ import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Timer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -70,11 +71,14 @@ public abstract class AbstractNodeMonitorDescriptor<T> extends Descriptor<Node ...@@ -70,11 +71,14 @@ public abstract class AbstractNodeMonitorDescriptor<T> extends Descriptor<Node
} }
private void schedule(long interval) { private void schedule(long interval) {
Trigger.timer.scheduleAtFixedRate(new SafeTimerTask() { Timer timer = Trigger.timer;
public void doRun() { if (timer != null) {
triggerUpdate(); timer.scheduleAtFixedRate(new SafeTimerTask() {
} public void doRun() {
}, interval, interval); triggerUpdate();
}
}, interval, interval);
}
} }
/** /**
......
...@@ -62,6 +62,8 @@ import java.util.logging.Level; ...@@ -62,6 +62,8 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import antlr.ANTLRException; import antlr.ANTLRException;
import javax.annotation.CheckForNull;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
/** /**
* Triggers a {@link Build}. * Triggers a {@link Build}.
...@@ -278,26 +280,32 @@ public abstract class Trigger<J extends Item> implements Describable<Trigger<?>> ...@@ -278,26 +280,32 @@ public abstract class Trigger<J extends Item> implements Describable<Trigger<?>>
* *
* If plugins want to run periodic jobs, they should implement {@link PeriodicWork}. * If plugins want to run periodic jobs, they should implement {@link PeriodicWork}.
*/ */
public static Timer timer; @SuppressWarnings("MS_SHOULD_BE_FINAL")
public static @CheckForNull Timer timer;
@Initializer(after=JOB_LOADED) @Initializer(after=JOB_LOADED)
public static void init() { public static void init() {
new DoubleLaunchChecker().schedule(); new DoubleLaunchChecker().schedule();
// start all PeridocWorks Timer _timer = timer;
for(PeriodicWork p : PeriodicWork.all()) if (_timer != null) {
timer.scheduleAtFixedRate(p,p.getInitialDelay(),p.getRecurrencePeriod()); // start all PeridocWorks
for(PeriodicWork p : PeriodicWork.all()) {
// start all AperidocWorks _timer.scheduleAtFixedRate(p,p.getInitialDelay(),p.getRecurrencePeriod());
for(AperiodicWork p : AperiodicWork.all())
timer.schedule(p,p.getInitialDelay());
// start monitoring nodes, although there's no hurry.
timer.schedule(new SafeTimerTask() {
public void doRun() {
ComputerSet.initialize();
} }
}, 1000*10);
// start all AperidocWorks
for(AperiodicWork p : AperiodicWork.all()) {
_timer.schedule(p,p.getInitialDelay());
}
// start monitoring nodes, although there's no hurry.
_timer.schedule(new SafeTimerTask() {
public void doRun() {
ComputerSet.initialize();
}
}, 1000*10);
}
} }
/** /**
......
...@@ -40,6 +40,7 @@ import java.util.logging.Logger; ...@@ -40,6 +40,7 @@ import java.util.logging.Logger;
import java.util.logging.Level; import java.util.logging.Level;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Timer;
/** /**
* Makes sure that no other Hudson uses our <tt>JENKINS_HOME</tt> directory, * Makes sure that no other Hudson uses our <tt>JENKINS_HOME</tt> directory,
...@@ -145,11 +146,14 @@ public class DoubleLaunchChecker { ...@@ -145,11 +146,14 @@ public class DoubleLaunchChecker {
public void schedule() { public void schedule() {
// randomize the scheduling so that multiple Hudson instances will write at the file at different time // randomize the scheduling so that multiple Hudson instances will write at the file at different time
long MINUTE = 1000*60; long MINUTE = 1000*60;
Trigger.timer.schedule(new SafeTimerTask() { Timer timer = Trigger.timer;
protected void doRun() { if (timer != null) {
execute(); timer.schedule(new SafeTimerTask() {
} protected void doRun() {
},(random.nextInt(30)+60)*MINUTE); execute();
}
},(random.nextInt(30)+60)*MINUTE);
}
} }
/** /**
......
...@@ -378,11 +378,11 @@ public abstract class FormFieldValidator { ...@@ -378,11 +378,11 @@ public abstract class FormFieldValidator {
return; return;
} }
String msg = ws.validateAntFileMask(value); String msg = ws.validateAntFileMask(value, 10000);
if(errorIfNotExist) error(msg); if(errorIfNotExist) error(msg);
else warning(msg); else warning(msg);
} catch (InterruptedException e) { } catch (InterruptedException e) {
ok(); // coundn't check ok(Messages.FormFieldValidator_did_not_manage_to_validate_may_be_too_sl(value));
} }
} }
......
...@@ -727,7 +727,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro ...@@ -727,7 +727,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
* @param pluginManager * @param pluginManager
* If non-null, use existing plugin manager. create a new one. * If non-null, use existing plugin manager. create a new one.
*/ */
@edu.umd.cs.findbugs.annotations.SuppressWarnings("SC_START_IN_CTOR") // bug in FindBugs. It flags UDPBroadcastThread.start() call but that's for another class @edu.umd.cs.findbugs.annotations.SuppressWarnings({
"SC_START_IN_CTOR", // bug in FindBugs. It flags UDPBroadcastThread.start() call but that's for another class
"ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD" // Trigger.timer
})
protected Jenkins(File root, ServletContext context, PluginManager pluginManager) throws IOException, InterruptedException, ReactorException { protected Jenkins(File root, ServletContext context, PluginManager pluginManager) throws IOException, InterruptedException, ReactorException {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
...@@ -816,12 +819,15 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro ...@@ -816,12 +819,15 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
} }
dnsMultiCast = new DNSMultiCast(this); dnsMultiCast = new DNSMultiCast(this);
Trigger.timer.scheduleAtFixedRate(new SafeTimerTask() { Timer timer = Trigger.timer;
@Override if (timer != null) {
protected void doRun() throws Exception { timer.scheduleAtFixedRate(new SafeTimerTask() {
trimLabels(); @Override
} protected void doRun() throws Exception {
}, TimeUnit2.MINUTES.toMillis(5), TimeUnit2.MINUTES.toMillis(5)); trimLabels();
}
}, TimeUnit2.MINUTES.toMillis(5), TimeUnit2.MINUTES.toMillis(5));
}
updateComputerList(); updateComputerList();
...@@ -2563,6 +2569,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro ...@@ -2563,6 +2569,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
/** /**
* Called to shut down the system. * Called to shut down the system.
*/ */
@edu.umd.cs.findbugs.annotations.SuppressWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
public void cleanUp() { public void cleanUp() {
for (ItemListener l : ItemListener.all()) for (ItemListener l : ItemListener.all())
l.onBeforeShutdown(); l.onBeforeShutdown();
...@@ -2579,7 +2586,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro ...@@ -2579,7 +2586,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
if(dnsMultiCast!=null) if(dnsMultiCast!=null)
dnsMultiCast.close(); dnsMultiCast.close();
interruptReloadThread(); interruptReloadThread();
Trigger.timer.cancel(); Timer timer = Trigger.timer;
if (timer != null) {
timer.cancel();
}
// TODO: how to wait for the completion of the last job? // TODO: how to wait for the completion of the last job?
Trigger.timer = null; Trigger.timer = null;
if(tcpSlaveAgentListener!=null) if(tcpSlaveAgentListener!=null)
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
FilePath.did_not_manage_to_validate_may_be_too_sl=Did not manage to validate {0} (may be too slow)
FilePath.validateAntFileMask.whitespaceSeprator=\ FilePath.validateAntFileMask.whitespaceSeprator=\
Whitespace can no longer be used as the separator. Please Use '','' as the separator instead. Whitespace can no longer be used as the separator. Please Use '','' as the separator instead.
FilePath.validateAntFileMask.doesntMatchAndSuggest=\ FilePath.validateAntFileMask.doesntMatchAndSuggest=\
......
...@@ -20,8 +20,11 @@ ...@@ -20,8 +20,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
SCMTrigger.DisplayName=Scruter l''outil de gestion de version SCMTrigger.DisplayName=Scrutation de l''outil de gestion de version
SCMTrigger.getDisplayName=Log du dernier accès à {0} SCMTrigger.getDisplayName=Log du dernier accès à {0}
SCMTrigger.SCMTriggerCause.ShortDescription=Un changement dans la base de code a provoqué le lancement de ce job SCMTrigger.BuildAction.DisplayName=Log de scrutation
SCMTrigger.SCMTriggerCause.ShortDescription=Lancé par un changement dans la base de code
TimerTrigger.DisplayName=Construire périodiquement TimerTrigger.DisplayName=Construire périodiquement
TimerTrigger.TimerTriggerCause.ShortDescription=L''\u00e9\u00e9ch\u00e9ance d''une alarme p\u00e9riodique a provoqu\u00e9 le lancement de ce job TimerTrigger.MissingWhitespace=Il semble manquer un espace entre * et *.
TimerTrigger.TimerTriggerCause.ShortDescription=Lancé par une alarme périodique
Trigger.init=Initialisation des minuteurs des déclencheurs
...@@ -24,6 +24,7 @@ ClockDifference.InSync=In sync ...@@ -24,6 +24,7 @@ ClockDifference.InSync=In sync
ClockDifference.Ahead=\ ahead ClockDifference.Ahead=\ ahead
ClockDifference.Behind=\ behind ClockDifference.Behind=\ behind
ClockDifference.Failed=Failed to check ClockDifference.Failed=Failed to check
FormFieldValidator.did_not_manage_to_validate_may_be_too_sl=Did not manage to validate {0} (may be too slow)
FormValidation.ValidateRequired=Required FormValidation.ValidateRequired=Required
FormValidation.Error.Details=(show details) FormValidation.Error.Details=(show details)
HttpResponses.Saved=Saved HttpResponses.Saved=Saved
\ No newline at end of file
...@@ -424,4 +424,55 @@ public class FilePathTest extends ChannelTestCase { ...@@ -424,4 +424,55 @@ public class FilePathTest extends ChannelTestCase {
assertEquals("/opt/jenkins/workspace/foo/bar/manchu", new FilePath(nixPath, "foo/bar\\manchu").getRemote()); assertEquals("/opt/jenkins/workspace/foo/bar/manchu", new FilePath(nixPath, "foo/bar\\manchu").getRemote());
assertEquals("/opt/jenkins/workspace/foo/bar/manchu", new FilePath(nixPath, "foo/bar/manchu").getRemote()); assertEquals("/opt/jenkins/workspace/foo/bar/manchu", new FilePath(nixPath, "foo/bar/manchu").getRemote());
} }
public void testValidateAntFileMask() throws Exception {
File tmp = Util.createTempDir();
try {
FilePath d = new FilePath(french, tmp.getPath());
d.child("d1/d2/d3").mkdirs();
d.child("d1/d2/d3/f.txt").touch(0);
d.child("d1/d2/d3/f.html").touch(0);
d.child("d1/d2/f.txt").touch(0);
assertValidateAntFileMask(null, d, "**/*.txt");
assertValidateAntFileMask(null, d, "d1/d2/d3/f.txt");
assertValidateAntFileMask(null, d, "**/*.html");
assertValidateAntFileMask(Messages.FilePath_validateAntFileMask_portionMatchButPreviousNotMatchAndSuggest("**/*.js", "**", "**/*.js"), d, "**/*.js");
assertValidateAntFileMask(Messages.FilePath_validateAntFileMask_doesntMatchAnything("index.htm"), d, "index.htm");
assertValidateAntFileMask(Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest("f.html", "d1/d2/d3/f.html"), d, "f.html");
// XXX lots more to test, e.g. multiple patterns separated by commas; ought to have full code coverage for this method
} finally {
Util.deleteRecursive(tmp);
}
}
private static void assertValidateAntFileMask(String expected, FilePath d, String fileMasks) throws Exception {
assertEquals(expected, d.validateAntFileMask(fileMasks));
}
@Bug(7214)
public void testValidateAntFileMaskBounded() throws Exception {
File tmp = Util.createTempDir();
try {
FilePath d = new FilePath(french, tmp.getPath());
FilePath d2 = d.child("d1/d2");
d2.mkdirs();
for (int i = 0; i < 100; i++) {
FilePath d3 = d2.child("d" + i);
d3.mkdirs();
d3.child("f.txt").touch(0);
}
assertEquals(null, d.validateAntFileMask("d1/d2/**/f.txt"));
assertEquals(null, d.validateAntFileMask("d1/d2/**/f.txt", 10));
assertEquals(Messages.FilePath_validateAntFileMask_portionMatchButPreviousNotMatchAndSuggest("**/*.js", "**", "**/*.js"), d.validateAntFileMask("**/*.js", 1000));
try {
d.validateAntFileMask("**/*.js", 10);
fail();
} catch (InterruptedException x) {
// good
}
} finally {
Util.deleteRecursive(tmp);
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册