Repository: incubator-distributedlog Updated Branches: refs/heads/master c44e0278e -> 9c6c9c452
DL-199: Be able to support filesystem-path like name In order to support hierarchical namespace, we need to be able to support filesystem path like log name. Author: Sijie Guo <si...@apache.org> Reviewers: Jia Zhai <None>, Leigh Stewart <lstew...@apache.org> Closes #130 from sijie/DL_199 Project: http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/commit/9c6c9c45 Tree: http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/tree/9c6c9c45 Diff: http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/diff/9c6c9c45 Branch: refs/heads/master Commit: 9c6c9c4520957499e305adf0658dd8711e99491b Parents: c44e027 Author: Sijie Guo <si...@apache.org> Authored: Mon Jun 12 12:55:41 2017 -0700 Committer: Sijie Guo <si...@apache.org> Committed: Mon Jun 12 12:55:41 2017 -0700 ---------------------------------------------------------------------- .../BKDistributedLogNamespace.java | 10 +- .../distributedlog/impl/BKNamespaceDriver.java | 4 +- .../org/apache/distributedlog/util/DLUtils.java | 106 ++++++++++++++----- .../org/apache/distributedlog/util/Utils.java | 1 + .../TestBKDistributedLogNamespace.java | 37 ++++++- .../apache/distributedlog/util/TestDLUtils.java | 34 ++++++ 6 files changed, 152 insertions(+), 40 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/blob/9c6c9c45/distributedlog-core/src/main/java/org/apache/distributedlog/BKDistributedLogNamespace.java ---------------------------------------------------------------------- diff --git a/distributedlog-core/src/main/java/org/apache/distributedlog/BKDistributedLogNamespace.java b/distributedlog-core/src/main/java/org/apache/distributedlog/BKDistributedLogNamespace.java index 0a4608e..adb591f 100644 --- a/distributedlog-core/src/main/java/org/apache/distributedlog/BKDistributedLogNamespace.java +++ b/distributedlog-core/src/main/java/org/apache/distributedlog/BKDistributedLogNamespace.java @@ -49,7 +49,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.distributedlog.namespace.NamespaceDriver.Role.WRITER; -import static org.apache.distributedlog.util.DLUtils.validateName; +import static org.apache.distributedlog.util.DLUtils.validateAndNormalizeName; /** * BKDistributedLogNamespace is the default implementation of {@link DistributedLogNamespace}. It uses @@ -148,7 +148,7 @@ public class BKDistributedLogNamespace implements DistributedLogNamespace { public void createLog(String logName) throws InvalidStreamNameException, IOException { checkState(); - validateName(logName); + logName = validateAndNormalizeName(logName); URI uri = FutureUtils.result(driver.getLogMetadataStore().createLog(logName)); FutureUtils.result(driver.getLogStreamMetadataStore(WRITER).getLog(uri, logName, true, true)); } @@ -157,7 +157,7 @@ public class BKDistributedLogNamespace implements DistributedLogNamespace { public void deleteLog(String logName) throws InvalidStreamNameException, LogNotFoundException, IOException { checkState(); - validateName(logName); + logName = validateAndNormalizeName(logName); Optional<URI> uri = FutureUtils.result(driver.getLogMetadataStore().getLogLocation(logName)); if (!uri.isPresent()) { throw new LogNotFoundException("Log " + logName + " isn't found."); @@ -186,7 +186,7 @@ public class BKDistributedLogNamespace implements DistributedLogNamespace { Optional<StatsLogger> perStreamStatsLogger) throws InvalidStreamNameException, IOException { checkState(); - validateName(logName); + logName = validateAndNormalizeName(logName); Optional<URI> uri = FutureUtils.result(driver.getLogMetadataStore().getLogLocation(logName)); if (!uri.isPresent()) { throw new LogNotFoundException("Log " + logName + " isn't found."); @@ -256,7 +256,7 @@ public class BKDistributedLogNamespace implements DistributedLogNamespace { throws InvalidStreamNameException, IOException { // Make sure the name is well formed checkState(); - validateName(nameOfLogStream); + nameOfLogStream = validateAndNormalizeName(nameOfLogStream); DistributedLogConfiguration mergedConfiguration = new DistributedLogConfiguration(); mergedConfiguration.addConfiguration(conf); http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/blob/9c6c9c45/distributedlog-core/src/main/java/org/apache/distributedlog/impl/BKNamespaceDriver.java ---------------------------------------------------------------------- diff --git a/distributedlog-core/src/main/java/org/apache/distributedlog/impl/BKNamespaceDriver.java b/distributedlog-core/src/main/java/org/apache/distributedlog/impl/BKNamespaceDriver.java index e132b64..dbe5400 100644 --- a/distributedlog-core/src/main/java/org/apache/distributedlog/impl/BKNamespaceDriver.java +++ b/distributedlog-core/src/main/java/org/apache/distributedlog/impl/BKNamespaceDriver.java @@ -74,7 +74,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.apache.distributedlog.util.DLUtils.isReservedStreamName; -import static org.apache.distributedlog.util.DLUtils.validateName; +import static org.apache.distributedlog.util.DLUtils.validateAndNormalizeName; /** * Manager for ZooKeeper/BookKeeper based namespace @@ -504,7 +504,7 @@ public class BKNamespaceDriver implements NamespaceDriver { throw new UnsupportedOperationException(); } checkState(); - validateName(streamName); + streamName = validateAndNormalizeName(streamName); return new ZKMetadataAccessor( streamName, conf, http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/blob/9c6c9c45/distributedlog-core/src/main/java/org/apache/distributedlog/util/DLUtils.java ---------------------------------------------------------------------- diff --git a/distributedlog-core/src/main/java/org/apache/distributedlog/util/DLUtils.java b/distributedlog-core/src/main/java/org/apache/distributedlog/util/DLUtils.java index 7b7e0f7..7231105 100644 --- a/distributedlog-core/src/main/java/org/apache/distributedlog/util/DLUtils.java +++ b/distributedlog-core/src/main/java/org/apache/distributedlog/util/DLUtils.java @@ -281,41 +281,89 @@ public class DLUtils { } /** - * Validate the stream name. + * Validate the log name. * - * @param nameOfStream - * name of stream + * @param logName + * name of log * @throws InvalidStreamNameException */ - public static void validateName(String nameOfStream) + public static String validateAndNormalizeName(String logName) throws InvalidStreamNameException { - String reason = null; - char chars[] = nameOfStream.toCharArray(); - char c; - // validate the stream to see if meet zookeeper path's requirement - for (int i = 0; i < chars.length; i++) { - c = chars[i]; - - if (c == 0) { - reason = "null character not allowed @" + i; - break; - } else if (c == '/') { - reason = "'/' not allowed @" + i; - break; - } else if (c > '\u0000' && c < '\u001f' - || c > '\u007f' && c < '\u009F' - || c > '\ud800' && c < '\uf8ff' - || c > '\ufff0' && c < '\uffff') { - reason = "invalid charater @" + i; - break; - } + if (isReservedStreamName(logName)) { + throw new InvalidStreamNameException(logName, "Log Name is reserved"); } - if (null != reason) { - throw new InvalidStreamNameException(nameOfStream, reason); + + if (logName.charAt(0) == '/') { + validatePathName(logName); + return logName.substring(1); + } else { + validatePathName("/" + logName); + return logName; } - if (isReservedStreamName(nameOfStream)) { - throw new InvalidStreamNameException(nameOfStream, - "Stream Name is reserved"); + } + + private static void validatePathName(String logName) throws InvalidStreamNameException { + if (logName == null) { + throw new InvalidStreamNameException("Log name cannot be null"); + } else if (logName.length() == 0) { + throw new InvalidStreamNameException("Log name length must be > 0"); + } else if (logName.charAt(0) != '/') { + throw new InvalidStreamNameException("Log name must start with / character"); + } else if (logName.length() != 1) { + if (logName.charAt(logName.length() - 1) == '/') { + throw new InvalidStreamNameException("Log name must not end with / character"); + } else { + String reason = null; + char lastc = '/'; + char[] chars = logName.toCharArray(); + + for (int i = 1; i < chars.length; ++i) { + char c = chars[i]; + if (c == 0) { + reason = "null character not allowed @" + i; + break; + } + + if (c == '<' || c == '>') { + reason = "< or > specified @" + i; + break; + } + + if (c == ' ') { + reason = "empty space specified @" + i; + break; + } + + if (c == '/' && lastc == '/') { + reason = "empty node name specified @" + i; + break; + } + + if (c == '.' && lastc == '.') { + if (chars[i - 2] == '/' && (i + 1 == chars.length || chars[i + 1] == '/')) { + reason = "relative paths not allowed @" + i; + break; + } + } else if (c == '.') { + if (chars[i - 1] == '/' && (i + 1 == chars.length || chars[i + 1] == '/')) { + reason = "relative paths not allowed @" + i; + break; + } + } else if (c > '\u0000' && c < '\u001f' + || c > '\u007f' && c < '\u009F' + || c > '\ud800' && c < '\uf8ff' + || c > '\ufff0' && c < '\uffff') { + reason = "invalid character @" + i; + break; + } + lastc = chars[i]; + } + + if (reason != null) { + throw new InvalidStreamNameException("Invalid log name \"" + logName + "\" caused by " + reason); + } + } } + } } http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/blob/9c6c9c45/distributedlog-core/src/main/java/org/apache/distributedlog/util/Utils.java ---------------------------------------------------------------------- diff --git a/distributedlog-core/src/main/java/org/apache/distributedlog/util/Utils.java b/distributedlog-core/src/main/java/org/apache/distributedlog/util/Utils.java index 7a09eeb..347f041 100644 --- a/distributedlog-core/src/main/java/org/apache/distributedlog/util/Utils.java +++ b/distributedlog-core/src/main/java/org/apache/distributedlog/util/Utils.java @@ -32,6 +32,7 @@ import com.google.common.io.Closeables; import org.apache.distributedlog.DistributedLogConstants; import org.apache.distributedlog.ZooKeeperClient; import org.apache.distributedlog.exceptions.DLInterruptedException; +import org.apache.distributedlog.exceptions.InvalidStreamNameException; import org.apache.distributedlog.exceptions.ZKException; import org.apache.distributedlog.function.VoidFunctions; import org.apache.distributedlog.io.AsyncCloseable; http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/blob/9c6c9c45/distributedlog-core/src/test/java/org/apache/distributedlog/TestBKDistributedLogNamespace.java ---------------------------------------------------------------------- diff --git a/distributedlog-core/src/test/java/org/apache/distributedlog/TestBKDistributedLogNamespace.java b/distributedlog-core/src/test/java/org/apache/distributedlog/TestBKDistributedLogNamespace.java index 43d1008..e0f2bab 100644 --- a/distributedlog-core/src/test/java/org/apache/distributedlog/TestBKDistributedLogNamespace.java +++ b/distributedlog-core/src/test/java/org/apache/distributedlog/TestBKDistributedLogNamespace.java @@ -22,7 +22,6 @@ import java.net.URI; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; -import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; @@ -52,7 +51,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.*; public class TestBKDistributedLogNamespace extends TestDistributedLogBase { @@ -82,6 +80,37 @@ public class TestBKDistributedLogNamespace extends TestDistributedLogBase { } @Test(timeout = 60000) + public void testCreateLogPath0() throws Exception { + createLogPathTest("/create/log/path/" + runtime.getMethodName()); + } + + @Test(timeout = 60000) + public void testCreateLogPath1() throws Exception { + createLogPathTest("create/log/path/" + runtime.getMethodName()); + } + + private void createLogPathTest(String logName) throws Exception { + URI uri = createDLMURI("/" + runtime.getMethodName()); + ensureURICreated(zooKeeperClient.get(), uri); + DistributedLogConfiguration newConf = new DistributedLogConfiguration(); + newConf.addConfiguration(conf); + newConf.setCreateStreamIfNotExists(false); + DistributedLogNamespace namespace = DistributedLogNamespaceBuilder.newBuilder() + .conf(newConf).uri(uri).build(); + DistributedLogManager dlm = namespace.openLog(logName); + LogWriter writer; + try { + writer = dlm.startLogSegmentNonPartitioned(); + writer.write(DLMTestUtil.getLogRecordInstance(1L)); + writer.flushAndSync(); + fail("Should fail to write data if stream doesn't exist."); + } catch (IOException ioe) { + // expected + } + dlm.close(); + } + + @Test(timeout = 60000) public void testCreateIfNotExists() throws Exception { URI uri = createDLMURI("/" + runtime.getMethodName()); ensureURICreated(zooKeeperClient.get(), uri); @@ -143,8 +172,8 @@ public class TestBKDistributedLogNamespace extends TestDistributedLogBase { } try { - namespace.openLog("/test2"); - fail("should fail to create invalid stream /test2"); + namespace.openLog("/ test2"); + fail("should fail to create invalid stream / test2"); } catch (InvalidStreamNameException isne) { // expected } http://git-wip-us.apache.org/repos/asf/incubator-distributedlog/blob/9c6c9c45/distributedlog-core/src/test/java/org/apache/distributedlog/util/TestDLUtils.java ---------------------------------------------------------------------- diff --git a/distributedlog-core/src/test/java/org/apache/distributedlog/util/TestDLUtils.java b/distributedlog-core/src/test/java/org/apache/distributedlog/util/TestDLUtils.java index df49d49..92bb6f9 100644 --- a/distributedlog-core/src/test/java/org/apache/distributedlog/util/TestDLUtils.java +++ b/distributedlog-core/src/test/java/org/apache/distributedlog/util/TestDLUtils.java @@ -21,12 +21,14 @@ import com.google.common.collect.Lists; import org.apache.distributedlog.DLMTestUtil; import org.apache.distributedlog.LogSegmentMetadata; import org.apache.distributedlog.LogSegmentMetadata.LogSegmentMetadataVersion; +import org.apache.distributedlog.exceptions.InvalidStreamNameException; import org.apache.distributedlog.exceptions.UnexpectedException; import org.junit.Test; import java.util.List; import static com.google.common.base.Charsets.UTF_8; +import static org.apache.distributedlog.util.DLUtils.validateAndNormalizeName; import static org.junit.Assert.*; /** @@ -270,4 +272,36 @@ public class TestDLUtils { DLUtils.bytes2LogSegmentId(corruptedData); } + @Test(timeout = 10000) + public void testValidateLogName() throws Exception { + String logName = "test-validate-log-name"; + validateAndNormalizeName(logName); + } + + @Test(timeout = 10000, expected = InvalidStreamNameException.class) + public void testValidateBadLogName0() throws Exception { + String logName = " test-bad-log-name"; + validateAndNormalizeName(logName); + } + + @Test(timeout = 10000, expected = InvalidStreamNameException.class) + public void testValidateBadLogName1() throws Exception { + String logName = "test-bad-log-name/"; + validateAndNormalizeName(logName); + } + + @Test(timeout = 10000, expected = InvalidStreamNameException.class) + public void testValidateBadLogName2() throws Exception { + String logName = "../test-bad-log-name/"; + validateAndNormalizeName(logName); + } + + @Test(timeout = 10000) + public void testValidateSameStreamPath() throws Exception { + String logName1 = "/test-resolve-log"; + String logName2 = "test-resolve-log"; + validateAndNormalizeName(logName1); + validateAndNormalizeName(logName2); + } + }