Merge remote-tracking branch 'origin/master' into jetty-http2
This commit is contained in:
commit
6eceb6bd99
|
@ -820,7 +820,6 @@ public class SslConnection extends AbstractConnection
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
getEndPoint().close();
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.util.security.Password;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
/**
|
||||
* Some requests for static data that is served by ResourceHandler, but some is secured.
|
||||
* <p>
|
||||
* This is mainly here to test security bypass techniques using aliased names that should be caught.
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class AliasedConstraintTest
|
||||
{
|
||||
private static final String TEST_REALM = "TestRealm";
|
||||
private static Server server;
|
||||
private static LocalConnector connector;
|
||||
private static ConstraintSecurityHandler security;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new LocalConnector(server);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
ContextHandler context = new ContextHandler();
|
||||
SessionHandler session = new SessionHandler();
|
||||
|
||||
HashLoginService loginService = new HashLoginService(TEST_REALM);
|
||||
loginService.putUser("user0",new Password("password"),new String[] {});
|
||||
loginService.putUser("user",new Password("password"),new String[] { "user" });
|
||||
loginService.putUser("user2",new Password("password"),new String[] { "user" });
|
||||
loginService.putUser("admin",new Password("password"),new String[] { "user", "administrator" });
|
||||
loginService.putUser("user3",new Password("password"),new String[] { "foo" });
|
||||
|
||||
context.setContextPath("/ctx");
|
||||
context.setResourceBase(MavenTestingUtils.getTestResourceDir("docroot").getAbsolutePath());
|
||||
server.setHandler(context);
|
||||
context.setHandler(session);
|
||||
// context.addAliasCheck(new AllowSymLinkAliasChecker());
|
||||
|
||||
server.addBean(loginService);
|
||||
|
||||
security = new ConstraintSecurityHandler();
|
||||
session.setHandler(security);
|
||||
ResourceHandler handler = new ResourceHandler();
|
||||
security.setHandler(handler);
|
||||
|
||||
List<ConstraintMapping> constraints = new ArrayList<>();
|
||||
|
||||
Constraint constraint0 = new Constraint();
|
||||
constraint0.setAuthenticate(true);
|
||||
constraint0.setName("forbid");
|
||||
ConstraintMapping mapping0 = new ConstraintMapping();
|
||||
mapping0.setPathSpec("/forbid/*");
|
||||
mapping0.setConstraint(constraint0);
|
||||
constraints.add(mapping0);
|
||||
|
||||
Set<String> knownRoles = new HashSet<>();
|
||||
knownRoles.add("user");
|
||||
knownRoles.add("administrator");
|
||||
|
||||
security.setConstraintMappings(constraints,knownRoles);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Parameters(name = "{0}: {1}")
|
||||
public static Collection<Object[]> data()
|
||||
{
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
final String OPENCONTENT = "this is open content";
|
||||
|
||||
data.add(new Object[] { "/ctx/all/index.txt", HttpStatus.OK_200, OPENCONTENT });
|
||||
data.add(new Object[] { "/ctx/ALL/index.txt", HttpStatus.NOT_FOUND_404, null });
|
||||
data.add(new Object[] { "/ctx/ALL/Fred/../index.txt", HttpStatus.NOT_FOUND_404, null });
|
||||
data.add(new Object[] { "/ctx/../bar/../ctx/all/index.txt", HttpStatus.OK_200, OPENCONTENT });
|
||||
data.add(new Object[] { "/ctx/forbid/index.txt", HttpStatus.FORBIDDEN_403, null });
|
||||
data.add(new Object[] { "/ctx/all/../forbid/index.txt", HttpStatus.FORBIDDEN_403, null });
|
||||
data.add(new Object[] { "/ctx/FoRbId/index.txt", HttpStatus.NOT_FOUND_404, null });
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Parameter(value = 0)
|
||||
public String uri;
|
||||
|
||||
@Parameter(value = 1)
|
||||
public int expectedStatusCode;
|
||||
|
||||
@Parameter(value = 2)
|
||||
public String expectedContent;
|
||||
|
||||
@Test
|
||||
public void testAccess() throws Exception
|
||||
{
|
||||
StringBuilder request = new StringBuilder();
|
||||
request.append("GET ").append(uri).append(" HTTP/1.1\r\n");
|
||||
request.append("Host: localhost\r\n");
|
||||
request.append("Connection: close\r\n");
|
||||
request.append("\r\n");
|
||||
|
||||
String response = connector.getResponses(request.toString());
|
||||
|
||||
switch (expectedStatusCode)
|
||||
{
|
||||
case 200:
|
||||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||
break;
|
||||
case 403:
|
||||
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
|
||||
break;
|
||||
case 404:
|
||||
assertThat(response,startsWith("HTTP/1.1 404 Not Found"));
|
||||
break;
|
||||
default:
|
||||
fail("Write a handler for response status code: " + expectedStatusCode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (expectedContent != null)
|
||||
{
|
||||
assertThat(response,containsString("this is open content"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
this is open content.
|
|
@ -0,0 +1 @@
|
|||
this is forbidden content.
|
|
@ -421,11 +421,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
if (_sendCallback.isInUse())
|
||||
{
|
||||
LOG.warn("Closed with pending write:"+this);
|
||||
_sendCallback.failed(new EofException("Connection closed"));
|
||||
}
|
||||
_sendCallback.close();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
|
@ -480,6 +476,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
_header = null;
|
||||
_shutdownOut = false;
|
||||
}
|
||||
else if (isClosed())
|
||||
{
|
||||
callback.failed(new EofException());
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.failed(new WritePendingException());
|
||||
|
|
|
@ -1648,10 +1648,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param path
|
||||
* @param resource
|
||||
* @return True if the alias is OK
|
||||
*/
|
||||
public boolean checkAlias(String path, Resource resource)
|
||||
{
|
||||
// Is the resource aliased?
|
||||
if (resource.getAlias() != null)
|
||||
if (resource.getAlias() != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
|
||||
|
|
|
@ -308,15 +308,16 @@ public class ResourceHandler extends HandlerWrapper
|
|||
{
|
||||
if (_context==null)
|
||||
return null;
|
||||
base=_context.getBaseResource();
|
||||
if (base==null)
|
||||
return null;
|
||||
return _context.getResource(path);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
path=URIUtil.canonicalPath(path);
|
||||
return base.addPath(path);
|
||||
Resource r = base.addPath(path);
|
||||
if (r!=null && r.getAlias()!=null && !_context.checkAlias(path, r))
|
||||
return null;
|
||||
return r;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
|
|
@ -96,15 +96,11 @@ public class PathMatchers
|
|||
|
||||
// If the pattern starts with a root path then its assumed to
|
||||
// be a full system path
|
||||
for (Path root : fs.getRootDirectories())
|
||||
if (isAbsolute(pattern))
|
||||
{
|
||||
StartLog.debug("root: " + root);
|
||||
if (pattern.startsWith(root.toString()))
|
||||
{
|
||||
String pat = "glob:" + pattern;
|
||||
StartLog.debug("Using absolute path pattern: " + pat);
|
||||
return fs.getPathMatcher(pat);
|
||||
}
|
||||
String pat = "glob:" + pattern;
|
||||
StartLog.debug("Using absolute path pattern: " + pat);
|
||||
return fs.getPathMatcher(pat);
|
||||
}
|
||||
|
||||
// Doesn't start with filesystem root, then assume the pattern
|
||||
|
|
|
@ -664,6 +664,8 @@ public class StartArgs
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartLog.debug("parse(\"%s\", \"%s\", %b)",rawarg,source,replaceProps);
|
||||
|
||||
final String arg = rawarg.trim();
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ public class ConfigurationAssert
|
|||
*/
|
||||
public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException
|
||||
{
|
||||
File testResourcesDir = MavenTestingUtils.getTestResourcesDir();
|
||||
Path testResourcesDir = MavenTestingUtils.getTestResourcesDir().toPath().toAbsolutePath();
|
||||
File file = MavenTestingUtils.getTestResourceFile(filename);
|
||||
TextFile textFile = new TextFile(file.toPath());
|
||||
|
||||
|
@ -149,12 +149,17 @@ public class ConfigurationAssert
|
|||
assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles);
|
||||
}
|
||||
|
||||
private static String shorten(BaseHome baseHome, Path path, File testResourcesDir)
|
||||
private static String shorten(BaseHome baseHome, Path path, Path testResourcesDir)
|
||||
{
|
||||
String value = baseHome.toShortForm(path);
|
||||
if (value.startsWith(testResourcesDir.getAbsolutePath()))
|
||||
if (value.startsWith("${"))
|
||||
{
|
||||
int len = testResourcesDir.getAbsolutePath().length();
|
||||
return value;
|
||||
}
|
||||
|
||||
if (path.startsWith(testResourcesDir))
|
||||
{
|
||||
int len = testResourcesDir.toString().length();
|
||||
value = "${maven-test-resources}" + value.substring(len);
|
||||
}
|
||||
return value;
|
||||
|
|
|
@ -18,20 +18,27 @@
|
|||
|
||||
package org.eclipse.jetty.start;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Assert;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MainTest
|
||||
{
|
||||
@Rule
|
||||
public TestTracker ttracker = new TestTracker();
|
||||
|
||||
@Before
|
||||
public void clearSystemProperties()
|
||||
{
|
||||
|
@ -70,9 +77,9 @@ public class MainTest
|
|||
System.err.println(args);
|
||||
|
||||
// Assert.assertEquals("--stop should not build module tree", 0, args.getEnabledModules().size());
|
||||
Assert.assertEquals("--stop missing port","10000",args.getProperties().getString("STOP.PORT"));
|
||||
Assert.assertEquals("--stop missing key","foo",args.getProperties().getString("STOP.KEY"));
|
||||
Assert.assertEquals("--stop missing wait","300",args.getProperties().getString("STOP.WAIT"));
|
||||
assertEquals("--stop missing port","10000",args.getProperties().getString("STOP.PORT"));
|
||||
assertEquals("--stop missing key","foo",args.getProperties().getString("STOP.KEY"));
|
||||
assertEquals("--stop missing wait","300",args.getProperties().getString("STOP.WAIT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -114,9 +121,22 @@ public class MainTest
|
|||
cmdLineArgs.add("-Xmx1024m");
|
||||
|
||||
// Arbitrary Libs
|
||||
File extraJar = MavenTestingUtils.getTestResourceFile("extra-libs/example.jar");
|
||||
File extraDir = MavenTestingUtils.getTestResourceDir("extra-resources");
|
||||
cmdLineArgs.add(String.format("--lib=%s%s%s",extraJar.getAbsolutePath(),File.pathSeparatorChar,extraDir.getAbsolutePath()));
|
||||
Path extraJar = MavenTestingUtils.getTestResourceFile("extra-libs/example.jar").toPath().normalize();
|
||||
Path extraDir = MavenTestingUtils.getTestResourceDir("extra-resources").toPath().normalize();
|
||||
|
||||
extraJar = extraJar.toAbsolutePath();
|
||||
extraDir = extraDir.toAbsolutePath();
|
||||
|
||||
assertThat("Extra Jar exists: " + extraJar,Files.exists(extraJar),is(true));
|
||||
assertThat("Extra Dir exists: " + extraDir,Files.exists(extraDir),is(true));
|
||||
|
||||
StringBuilder lib = new StringBuilder();
|
||||
lib.append("--lib=");
|
||||
lib.append(extraJar.toString());
|
||||
lib.append(File.pathSeparator);
|
||||
lib.append(extraDir.toString());
|
||||
|
||||
cmdLineArgs.add(lib.toString());
|
||||
|
||||
// Arbitrary XMLs
|
||||
cmdLineArgs.add("jetty.xml");
|
||||
|
@ -128,8 +148,8 @@ public class MainTest
|
|||
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
|
||||
BaseHome baseHome = main.getBaseHome();
|
||||
|
||||
Assert.assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
|
||||
Assert.assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
|
||||
assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
|
||||
assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
|
||||
|
||||
ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-jvm.txt");
|
||||
}
|
||||
|
@ -154,8 +174,8 @@ public class MainTest
|
|||
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
|
||||
BaseHome baseHome = main.getBaseHome();
|
||||
|
||||
Assert.assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
|
||||
Assert.assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
|
||||
assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
|
||||
assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
|
||||
|
||||
ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-spdy.txt");
|
||||
}
|
||||
|
@ -173,8 +193,8 @@ public class MainTest
|
|||
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
|
||||
BaseHome baseHome = main.getBaseHome();
|
||||
|
||||
Assert.assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
|
||||
Assert.assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
|
||||
assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
|
||||
assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
|
||||
|
||||
ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-spaces.txt");
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
|
@ -50,6 +51,46 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
*/
|
||||
public abstract class IteratingCallback implements Callback
|
||||
{
|
||||
|
||||
/**
|
||||
* The internal states of this callback
|
||||
*/
|
||||
private enum State
|
||||
{
|
||||
/**
|
||||
* This callback is inactive, ready to iterate.
|
||||
*/
|
||||
INACTIVE,
|
||||
/**
|
||||
* This callback is iterating and {@link #process()} has scheduled an
|
||||
* asynchronous operation by returning {@link Action#SCHEDULED}, but
|
||||
* the operation is still undergoing.
|
||||
*/
|
||||
ACTIVE,
|
||||
/**
|
||||
* This callback is iterating and {@link #process()} has been called
|
||||
* but not returned yet.
|
||||
*/
|
||||
ITERATING,
|
||||
/**
|
||||
* While this callback was iterating, another request for iteration
|
||||
* has been issued, so the iteration must continue even if a previous
|
||||
* call to {@link #process()} returned {@link Action#IDLE}.
|
||||
*/
|
||||
ITERATE_AGAIN,
|
||||
/**
|
||||
* The overall job has succeeded.
|
||||
*/
|
||||
SUCCEEDED,
|
||||
/**
|
||||
* The overall job has failed.
|
||||
*/
|
||||
FAILED,
|
||||
/**
|
||||
* The ICB has been closed and cannot be reset
|
||||
*/
|
||||
CLOSED
|
||||
}
|
||||
/**
|
||||
* The indication of the overall progress of the overall job that
|
||||
* implementations of {@link #process()} must return.
|
||||
|
@ -211,8 +252,25 @@ public abstract class IteratingCallback implements Callback
|
|||
case SUCCEEDED:
|
||||
{
|
||||
// The overall job has completed.
|
||||
completeSuccess();
|
||||
return true;
|
||||
while (true)
|
||||
{
|
||||
State current = _state.get();
|
||||
switch(current)
|
||||
{
|
||||
case SUCCEEDED:
|
||||
case FAILED:
|
||||
// Already complete!.
|
||||
return true;
|
||||
case CLOSED:
|
||||
throw new IllegalStateException();
|
||||
default:
|
||||
if (_state.compareAndSet(current, State.SUCCEEDED))
|
||||
{
|
||||
onCompleteSuccess();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
@ -260,6 +318,10 @@ public abstract class IteratingCallback implements Callback
|
|||
iterate();
|
||||
return;
|
||||
}
|
||||
case CLOSED:
|
||||
// too late!
|
||||
return;
|
||||
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException(toString());
|
||||
|
@ -275,32 +337,6 @@ public abstract class IteratingCallback implements Callback
|
|||
*/
|
||||
@Override
|
||||
public final void failed(Throwable x)
|
||||
{
|
||||
completeFailure(x);
|
||||
}
|
||||
|
||||
protected void completeSuccess()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
State current = _state.get();
|
||||
switch(current)
|
||||
{
|
||||
case SUCCEEDED:
|
||||
case FAILED:
|
||||
// Already complete!.
|
||||
return;
|
||||
default:
|
||||
if (_state.compareAndSet(current, State.SUCCEEDED))
|
||||
{
|
||||
onCompleteSuccess();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void completeFailure(Throwable x)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
@ -309,8 +345,11 @@ public abstract class IteratingCallback implements Callback
|
|||
{
|
||||
case SUCCEEDED:
|
||||
case FAILED:
|
||||
case INACTIVE:
|
||||
case CLOSED:
|
||||
// Already complete!.
|
||||
return;
|
||||
|
||||
default:
|
||||
if (_state.compareAndSet(current, State.FAILED))
|
||||
{
|
||||
|
@ -321,31 +360,43 @@ public abstract class IteratingCallback implements Callback
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this callback is idle and {@link #iterate()} needs to be called
|
||||
*/
|
||||
public boolean isIdle()
|
||||
public final void close()
|
||||
{
|
||||
return _state.get() == State.INACTIVE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return true if the callback is not INACTIVE, FAILED or SUCCEEDED.
|
||||
*/
|
||||
public boolean isInUse()
|
||||
{
|
||||
switch(_state.get())
|
||||
while (true)
|
||||
{
|
||||
case INACTIVE:
|
||||
case FAILED:
|
||||
case SUCCEEDED:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
State current = _state.get();
|
||||
switch(current)
|
||||
{
|
||||
case INACTIVE:
|
||||
case SUCCEEDED:
|
||||
case FAILED:
|
||||
if (_state.compareAndSet(current, State.CLOSED))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
if (_state.compareAndSet(current, State.CLOSED))
|
||||
{
|
||||
onCompleteFailure(new IllegalStateException("Closed with pending callback "+current));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* only for testing
|
||||
* @return whether this callback is idle and {@link #iterate()} needs to be called
|
||||
*/
|
||||
boolean isIdle()
|
||||
{
|
||||
return _state.get() == State.INACTIVE;
|
||||
}
|
||||
|
||||
public boolean isClosed()
|
||||
{
|
||||
return _state.get() == State.CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this callback has failed
|
||||
*/
|
||||
|
@ -397,40 +448,4 @@ public abstract class IteratingCallback implements Callback
|
|||
{
|
||||
return String.format("%s[%s]", super.toString(), _state);
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal states of this callback
|
||||
*/
|
||||
private enum State
|
||||
{
|
||||
/**
|
||||
* This callback is inactive, ready to iterate.
|
||||
*/
|
||||
INACTIVE,
|
||||
/**
|
||||
* This callback is iterating and {@link #process()} has scheduled an
|
||||
* asynchronous operation by returning {@link Action#SCHEDULED}, but
|
||||
* the operation is still undergoing.
|
||||
*/
|
||||
ACTIVE,
|
||||
/**
|
||||
* This callback is iterating and {@link #process()} has been called
|
||||
* but not returned yet.
|
||||
*/
|
||||
ITERATING,
|
||||
/**
|
||||
* While this callback was iterating, another request for iteration
|
||||
* has been issued, so the iteration must continue even if a previous
|
||||
* call to {@link #process()} returned {@link Action#IDLE}.
|
||||
*/
|
||||
ITERATE_AGAIN,
|
||||
/**
|
||||
* The overall job has succeeded.
|
||||
*/
|
||||
SUCCEEDED,
|
||||
/**
|
||||
* The overall job has failed.
|
||||
*/
|
||||
FAILED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,12 +83,18 @@ public abstract class AbstractFSResourceTest
|
|||
{
|
||||
if (OS.IS_UNIX)
|
||||
{
|
||||
// A windows path is invalid under unix
|
||||
newResource(new URI("file://Z:/:"));
|
||||
}
|
||||
else if (OS.IS_WINDOWS)
|
||||
{
|
||||
newResource(new URI("file://some\"text\""));
|
||||
// "CON" is a reserved name under windows
|
||||
newResource(new URI("file://CON"));
|
||||
}
|
||||
else
|
||||
{
|
||||
assumeFalse("Unknown OS type",false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue