aboutsummaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
authorkenton@google.com <kenton@google.com@630680e5-0e50-0410-840e-4b1c322b438d>2009-04-25 02:53:47 +0000
committerkenton@google.com <kenton@google.com@630680e5-0e50-0410-840e-4b1c322b438d>2009-04-25 02:53:47 +0000
commitd37d46dfbcedadeb439ad0367f8afcf8867dca43 (patch)
treeb896df229f7c671637924c156d5a759ba50a3190 /java/src
parent709ea28f3264aa5632e5577a4080671173fc6166 (diff)
downloadprotobuf-d37d46dfbcedadeb439ad0367f8afcf8867dca43.tar.gz
protobuf-d37d46dfbcedadeb439ad0367f8afcf8867dca43.tar.bz2
protobuf-d37d46dfbcedadeb439ad0367f8afcf8867dca43.zip
Integrate recent changes from Google-internal code tree. See CHANGES.txt
for details.
Diffstat (limited to 'java/src')
-rw-r--r--java/src/main/java/com/google/protobuf/AbstractMessage.java58
-rw-r--r--java/src/main/java/com/google/protobuf/BlockingRpcChannel.java51
-rw-r--r--java/src/main/java/com/google/protobuf/BlockingService.java64
-rw-r--r--java/src/main/java/com/google/protobuf/CodedInputStream.java62
-rw-r--r--java/src/main/java/com/google/protobuf/Descriptors.java2
-rw-r--r--java/src/main/java/com/google/protobuf/DynamicMessage.java7
-rw-r--r--java/src/main/java/com/google/protobuf/Message.java43
-rw-r--r--java/src/main/java/com/google/protobuf/RpcUtil.java6
-rw-r--r--java/src/main/java/com/google/protobuf/ServiceException.java42
-rw-r--r--java/src/main/java/com/google/protobuf/UnknownFieldSet.java45
-rw-r--r--java/src/test/java/com/google/protobuf/AbstractMessageTest.java38
-rw-r--r--java/src/test/java/com/google/protobuf/CodedInputStreamTest.java61
-rw-r--r--java/src/test/java/com/google/protobuf/DynamicMessageTest.java12
-rw-r--r--java/src/test/java/com/google/protobuf/GeneratedMessageTest.java11
-rw-r--r--java/src/test/java/com/google/protobuf/ServiceTest.java99
-rw-r--r--java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java99
-rw-r--r--java/src/test/java/com/google/protobuf/WireFormatTest.java20
17 files changed, 696 insertions, 24 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java
index 24c32a47..53175cdd 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -32,6 +32,7 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
+import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -152,6 +153,13 @@ public abstract class AbstractMessage implements Message {
codedOutput.flush();
}
+ public void writeDelimitedTo(OutputStream output) throws IOException {
+ CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
+ codedOutput.writeRawVarint32(getSerializedSize());
+ writeTo(codedOutput);
+ codedOutput.flush();
+ }
+
private int memoizedSize = -1;
public int getSerializedSize() {
@@ -207,7 +215,8 @@ public abstract class AbstractMessage implements Message {
if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
return false;
}
- return getAllFields().equals(otherMessage.getAllFields());
+ return getAllFields().equals(otherMessage.getAllFields()) &&
+ getUnknownFields().equals(otherMessage.getUnknownFields());
}
@Override
@@ -215,6 +224,7 @@ public abstract class AbstractMessage implements Message {
int hash = 41;
hash = (19 * hash) + getDescriptorForType().hashCode();
hash = (53 * hash) + getAllFields().hashCode();
+ hash = (29 * hash) + getUnknownFields().hashCode();
return hash;
}
@@ -397,5 +407,51 @@ public abstract class AbstractMessage implements Message {
codedInput.checkLastTagWas(0);
return (BuilderType) this;
}
+
+ public BuilderType mergeDelimitedFrom(InputStream input,
+ ExtensionRegistry extensionRegistry)
+ throws IOException {
+ final int size = CodedInputStream.readRawVarint32(input);
+
+ // A stream which will not read more than |size| bytes.
+ InputStream limitedInput = new FilterInputStream(input) {
+ int limit = size;
+
+ @Override
+ public int available() throws IOException {
+ return Math.min(super.available(), limit);
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (limit <= 0) return -1;
+ int result = super.read();
+ if (result >= 0) --limit;
+ return result;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (limit <= 0) return -1;
+ len = Math.min(len, limit);
+ int result = super.read(b, off, len);
+ if (result >= 0) limit -= result;
+ return result;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ long result = super.skip(Math.min(n, limit));
+ if (result >= 0) limit -= result;
+ return result;
+ }
+ };
+ return mergeFrom(limitedInput, extensionRegistry);
+ }
+
+ public BuilderType mergeDelimitedFrom(InputStream input)
+ throws IOException {
+ return mergeDelimitedFrom(input, ExtensionRegistry.getEmptyRegistry());
+ }
}
}
diff --git a/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java b/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java
new file mode 100644
index 00000000..1e81143a
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * <p>Abstract interface for a blocking RPC channel. {@code BlockingRpcChannel}
+ * is the blocking equivalent to {@link RpcChannel}.
+ *
+ * @author kenton@google.com Kenton Varda
+ * @author cpovirk@google.com Chris Povirk
+ */
+public interface BlockingRpcChannel {
+ /**
+ * Call the given method of the remote service and blocks until it returns.
+ * {@code callBlockingMethod()} is the blocking equivalent to
+ * {@link RpcChannel#callMethod}.
+ */
+ Message callBlockingMethod(
+ Descriptors.MethodDescriptor method,
+ RpcController controller,
+ Message request,
+ Message responsePrototype) throws ServiceException;
+}
diff --git a/java/src/main/java/com/google/protobuf/BlockingService.java b/java/src/main/java/com/google/protobuf/BlockingService.java
new file mode 100644
index 00000000..ecc80096
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/BlockingService.java
@@ -0,0 +1,64 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * Blocking equivalent to {@link Service}.
+ *
+ * @author kenton@google.com Kenton Varda
+ * @author cpovirk@google.com Chris Povirk
+ */
+public interface BlockingService {
+ /**
+ * Equivalent to {@link Service#getDescriptorForType}.
+ */
+ Descriptors.ServiceDescriptor getDescriptorForType();
+
+ /**
+ * Equivalent to {@link Service#callMethod}, except that
+ * {@code callBlockingMethod()} returns the result of the RPC or throws a
+ * {@link ServiceException} if there is a failure, rather than passing the
+ * information to a callback.
+ */
+ Message callBlockingMethod(Descriptors.MethodDescriptor method,
+ RpcController controller,
+ Message request) throws ServiceException;
+
+ /**
+ * Equivalent to {@link Service#getRequestPrototype}.
+ */
+ Message getRequestPrototype(Descriptors.MethodDescriptor method);
+
+ /**
+ * Equivalent to {@link Service#getResponsePrototype}.
+ */
+ Message getResponsePrototype(Descriptors.MethodDescriptor method);
+}
diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java
index 8f277a9f..b6be2e87 100644
--- a/java/src/main/java/com/google/protobuf/CodedInputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java
@@ -77,7 +77,7 @@ public final class CodedInputStream {
* may legally end wherever a tag occurs, and zero is not a valid tag number.
*/
public int readTag() throws IOException {
- if (bufferPos == bufferSize && !refillBuffer(false)) {
+ if (isAtEnd()) {
lastTag = 0;
return 0;
}
@@ -383,6 +383,39 @@ public final class CodedInputStream {
return result;
}
+ /**
+ * Reads a varint from the input one byte at a time, so that it does not
+ * read any bytes after the end of the varint. If you simply wrapped the
+ * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
+ * then you would probably end up reading past the end of the varint since
+ * CodedInputStream buffers its input.
+ */
+ static int readRawVarint32(InputStream input) throws IOException {
+ int result = 0;
+ int offset = 0;
+ for (; offset < 32; offset += 7) {
+ int b = input.read();
+ if (b == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ result |= (b & 0x7f) << offset;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ // Keep reading up to 64 bits.
+ for (; offset < 64; offset += 7) {
+ int b = input.read();
+ if (b == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
/** Read a raw Varint from the stream. */
public long readRawVarint64() throws IOException {
int shift = 0;
@@ -526,6 +559,10 @@ public final class CodedInputStream {
* size limits only apply when reading from an {@code InputStream}, not
* when constructed around a raw byte array (nor with
* {@link ByteString#newCodedInput}).
+ * <p>
+ * If you want to read several messages from a single CodedInputStream, you
+ * could call {@link #resetSizeCounter()} after each one to avoid hitting the
+ * size limit.
*
* @return the old limit.
*/
@@ -540,6 +577,13 @@ public final class CodedInputStream {
}
/**
+ * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
+ */
+ public void resetSizeCounter() {
+ totalBytesRetired = 0;
+ }
+
+ /**
* Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
* is called when descending into a length-delimited embedded message.
*
@@ -597,6 +641,15 @@ public final class CodedInputStream {
}
/**
+ * Returns true if the stream has reached the end of the input. This is the
+ * case if either the end of the underlying input source has been reached or
+ * if the stream has reached a limit created using {@link #pushLimit(int)}.
+ */
+ public boolean isAtEnd() throws IOException {
+ return bufferPos == bufferSize && !refillBuffer(false);
+ }
+
+ /**
* Called with {@code this.buffer} is empty to read more bytes from the
* input. If {@code mustSucceed} is true, refillBuffer() gurantees that
* either there will be at least one byte in the buffer when it returns
@@ -622,6 +675,11 @@ public final class CodedInputStream {
bufferPos = 0;
bufferSize = (input == null) ? -1 : input.read(buffer);
+ if (bufferSize == 0 || bufferSize < -1) {
+ throw new IllegalStateException(
+ "InputStream#read(byte[]) returned invalid result: " + bufferSize +
+ "\nThe InputStream implementation is buggy.");
+ }
if (bufferSize == -1) {
bufferSize = 0;
if (mustSucceed) {
@@ -778,7 +836,7 @@ public final class CodedInputStream {
throw InvalidProtocolBufferException.truncatedMessage();
}
- if (size < bufferSize - bufferPos) {
+ if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
bufferPos += size;
} else {
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java
index 80266a59..24499efb 100644
--- a/java/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/src/main/java/com/google/protobuf/Descriptors.java
@@ -1689,7 +1689,7 @@ public final class Descriptors {
GenericDescriptor old =
descriptorsByName.put(fullName,
- new PackageDescriptor(fullName, name, file));
+ new PackageDescriptor(name, fullName, file));
if (old != null) {
descriptorsByName.put(fullName, old);
if (!(old instanceof PackageDescriptor)) {
diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java
index 99ae253c..59fb7b02 100644
--- a/java/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -260,7 +260,8 @@ public final class DynamicMessage extends AbstractMessage {
}
public DynamicMessage build() {
- if (!isInitialized()) {
+ // If fields == null, we'll throw an appropriate exception later.
+ if (fields != null && !isInitialized()) {
throw new UninitializedMessageException(
new DynamicMessage(type, fields, unknownFields));
}
@@ -282,6 +283,10 @@ public final class DynamicMessage extends AbstractMessage {
}
public DynamicMessage buildPartial() {
+ if (fields == null) {
+ throw new IllegalStateException(
+ "build() has already been called on this Builder.");
+ }
fields.makeImmutable();
DynamicMessage result =
new DynamicMessage(type, fields, unknownFields);
diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java
index 2f8700db..a5951232 100644
--- a/java/src/main/java/com/google/protobuf/Message.java
+++ b/java/src/main/java/com/google/protobuf/Message.java
@@ -183,9 +183,28 @@ public interface Message {
* Serializes the message and writes it to {@code output}. This is just a
* trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does
* not flush or close the stream.
+ * <p>
+ * NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write
+ * any more data to the stream after the message, you must somehow ensure
+ * that the parser on the receiving end does not interpret this as being
+ * part of the protocol message. This can be done e.g. by writing the size
+ * of the message before the data, then making sure to limit the input to
+ * that size on the receiving end (e.g. by wrapping the InputStream in one
+ * which limits the input). Alternatively, just use
+ * {@link #writeDelimitedTo(OutputStream)}.
*/
void writeTo(OutputStream output) throws IOException;
+ /**
+ * Like {@link #writeTo(OutputStream)}, but writes the size of the message
+ * as a varint before writing the data. This allows more data to be written
+ * to the stream after the message without the need to delimit the message
+ * data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or
+ * the static method {@code YourMessageType.parseDelimitedFrom(InputStream)})
+ * to parse messages written by this method.
+ */
+ void writeDelimitedTo(OutputStream output) throws IOException;
+
// =================================================================
// Builders
@@ -434,8 +453,11 @@ public interface Message {
* {@link #mergeFrom(CodedInputStream)}. Note that this method always
* reads the <i>entire</i> input (unless it throws an exception). If you
* want it to stop earlier, you will need to wrap your input in some
- * wrapper stream that limits reading. Despite usually reading the entire
- * input, this does not close the stream.
+ * wrapper stream that limits reading. Or, use
+ * {@link Message#writeDelimitedTo(OutputStream)} to write your message and
+ * {@link #mergeDelimitedFrom(InputStream)} to read it.
+ * <p>
+ * Despite usually reading the entire input, this does not close the stream.
*/
Builder mergeFrom(InputStream input) throws IOException;
@@ -447,5 +469,22 @@ public interface Message {
Builder mergeFrom(InputStream input,
ExtensionRegistry extensionRegistry)
throws IOException;
+
+ /**
+ * Like {@link #mergeFrom(InputStream)}, but does not read until EOF.
+ * Instead, the size of the message (encoded as a varint) is read first,
+ * then the message data. Use
+ * {@link Message#writeDelimitedTo(OutputStream)} to write messages in this
+ * format.
+ */
+ Builder mergeDelimitedFrom(InputStream input)
+ throws IOException;
+
+ /**
+ * Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions.
+ */
+ Builder mergeDelimitedFrom(InputStream input,
+ ExtensionRegistry extensionRegistry)
+ throws IOException;
}
}
diff --git a/java/src/main/java/com/google/protobuf/RpcUtil.java b/java/src/main/java/com/google/protobuf/RpcUtil.java
index a11b0437..8144bbfb 100644
--- a/java/src/main/java/com/google/protobuf/RpcUtil.java
+++ b/java/src/main/java/com/google/protobuf/RpcUtil.java
@@ -39,7 +39,7 @@ public final class RpcUtil {
private RpcUtil() {}
/**
- * Take an {@code RcpCallabck<Message>} and convert it to an
+ * Take an {@code RpcCallback<Message>} and convert it to an
* {@code RpcCallback} accepting a specific message type. This is always
* type-safe (parameter type contravariance).
*/
@@ -58,8 +58,8 @@ public final class RpcUtil {
}
/**
- * Take an {@code RcpCallabck} accepting a specific message type and convert
- * it to an {@code RcpCallabck<Message>}. The generalized callback will
+ * Take an {@code RpcCallback} accepting a specific message type and convert
+ * it to an {@code RpcCallback<Message>}. The generalized callback will
* accept any message object which has the same descriptor, and will convert
* it to the correct class before calling the original callback. However,
* if the generalized callback is given a message with a different descriptor,
diff --git a/java/src/main/java/com/google/protobuf/ServiceException.java b/java/src/main/java/com/google/protobuf/ServiceException.java
new file mode 100644
index 00000000..70b9d0c1
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/ServiceException.java
@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * Thrown by blocking RPC methods when a failure occurs.
+ *
+ * @author cpovirk@google.com (Chris Povirk)
+ */
+public final class ServiceException extends Exception {
+ public ServiceException(String message) {
+ super(message);
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
index 445951b0..77fef323 100644
--- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
+++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
@@ -34,6 +34,7 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.TreeMap;
import java.util.List;
@@ -85,6 +86,20 @@ public final class UnknownFieldSet {
}
private Map<Integer, Field> fields;
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ return (other instanceof UnknownFieldSet) &&
+ this.fields.equals(((UnknownFieldSet) other).fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.fields.hashCode();
+ }
+
/** Get a map of fields in the set by number. */
public Map<Integer, Field> asMap() {
return fields;
@@ -540,6 +555,36 @@ public final class UnknownFieldSet {
*/
public List<UnknownFieldSet> getGroupList() { return group; }
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Field)) {
+ return false;
+ }
+ return Arrays.equals(this.getIdentityArray(),
+ ((Field) other).getIdentityArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(getIdentityArray());
+ }
+
+ /**
+ * Returns the array of objects to be used to uniquely identify this
+ * {@link UnknownFieldSet.Field} instance.
+ */
+ private Object[] getIdentityArray() {
+ return new Object[] {
+ this.varint,
+ this.fixed32,
+ this.fixed64,
+ this.lengthDelimited,
+ this.group};
+ }
+
/**
* Serializes the field, including field number, and writes it to
* {@code output}.
diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
index da073561..2c59fd0d 100644
--- a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
@@ -30,14 +30,14 @@
package com.google.protobuf;
+import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.ForeignMessage;
-import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestRequiredForeign;
-import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
import junit.framework.TestCase;
@@ -329,7 +329,7 @@ public class AbstractMessageTest extends TestCase {
// -----------------------------------------------------------------
// Tests for equals and hashCode
- public void testEqualsAndHashCode() {
+ public void testEqualsAndHashCode() throws Exception {
TestAllTypes a = TestUtil.getAllSet();
TestAllTypes b = TestAllTypes.newBuilder().build();
TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build();
@@ -337,7 +337,7 @@ public class AbstractMessageTest extends TestCase {
TestAllExtensions e = TestUtil.getAllExtensionsSet();
TestAllExtensions f = TestAllExtensions.newBuilder(e)
.addExtension(UnittestProto.repeatedInt32Extension, 999).build();
-
+
checkEqualsIsConsistent(a);
checkEqualsIsConsistent(b);
checkEqualsIsConsistent(c);
@@ -364,10 +364,25 @@ public class AbstractMessageTest extends TestCase {
checkNotEqual(d, f);
checkNotEqual(e, f);
+
+ // Deserializing into the TestEmptyMessage such that every field
+ // is an {@link UnknownFieldSet.Field}.
+ UnittestProto.TestEmptyMessage eUnknownFields =
+ UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
+ UnittestProto.TestEmptyMessage fUnknownFields =
+ UnittestProto.TestEmptyMessage.parseFrom(f.toByteArray());
+ checkNotEqual(eUnknownFields, fUnknownFields);
+ checkEqualsIsConsistent(eUnknownFields);
+ checkEqualsIsConsistent(fUnknownFields);
+
+ // Subseqent reconstitutions should be identical
+ UnittestProto.TestEmptyMessage eUnknownFields2 =
+ UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
+ checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
}
/**
- * Asserts that the given protos are equal and have the same hash code.
+ * Asserts that the given proto has symetric equals and hashCode methods.
*/
private void checkEqualsIsConsistent(Message message) {
// Object should be equal to itself.
@@ -375,9 +390,16 @@ public class AbstractMessageTest extends TestCase {
// Object should be equal to a dynamic copy of itself.
DynamicMessage dynamic = DynamicMessage.newBuilder(message).build();
- assertEquals(message, dynamic);
- assertEquals(dynamic, message);
- assertEquals(dynamic.hashCode(), message.hashCode());
+ checkEqualsIsConsistent(message, dynamic);
+ }
+
+ /**
+ * Asserts that the given protos are equal and have the same hash code.
+ */
+ private void checkEqualsIsConsistent(Message message1, Message message2) {
+ assertEquals(message1, message2);
+ assertEquals(message2, message1);
+ assertEquals(message2.hashCode(), message1.hashCode());
}
/**
diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
index eaaddf44..850b8aa7 100644
--- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
@@ -95,6 +95,7 @@ public class CodedInputStreamTest extends TestCase {
input = CodedInputStream.newInstance(data);
assertEquals(value, input.readRawVarint64());
+ assertTrue(input.isAtEnd());
// Try different block sizes.
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
@@ -105,7 +106,17 @@ public class CodedInputStreamTest extends TestCase {
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertEquals(value, input.readRawVarint64());
+ assertTrue(input.isAtEnd());
}
+
+ // Try reading direct from an InputStream. We want to verify that it
+ // doesn't read past the end of the input, so we copy to a new, bigger
+ // array first.
+ byte[] longerData = new byte[data.length + 1];
+ System.arraycopy(data, 0, longerData, 0, data.length);
+ InputStream rawInput = new ByteArrayInputStream(longerData);
+ assertEquals((int)value, CodedInputStream.readRawVarint32(rawInput));
+ assertEquals(1, rawInput.available());
}
/**
@@ -131,6 +142,14 @@ public class CodedInputStreamTest extends TestCase {
} catch (InvalidProtocolBufferException e) {
assertEquals(expected.getMessage(), e.getMessage());
}
+
+ // Make sure we get the same error when reading direct from an InputStream.
+ try {
+ CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
+ fail("Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(expected.getMessage(), e.getMessage());
+ }
}
/** Tests readRawVarint32() and readRawVarint64(). */
@@ -180,12 +199,14 @@ public class CodedInputStreamTest extends TestCase {
throws Exception {
CodedInputStream input = CodedInputStream.newInstance(data);
assertEquals(value, input.readRawLittleEndian32());
+ assertTrue(input.isAtEnd());
// Try different block sizes.
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertEquals(value, input.readRawLittleEndian32());
+ assertTrue(input.isAtEnd());
}
}
@@ -197,12 +218,14 @@ public class CodedInputStreamTest extends TestCase {
throws Exception {
CodedInputStream input = CodedInputStream.newInstance(data);
assertEquals(value, input.readRawLittleEndian64());
+ assertTrue(input.isAtEnd());
// Try different block sizes.
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
input = CodedInputStream.newInstance(
new SmallBlockInputStream(data, blockSize));
assertEquals(value, input.readRawLittleEndian64());
+ assertTrue(input.isAtEnd());
}
}
@@ -288,6 +311,20 @@ public class CodedInputStreamTest extends TestCase {
}
}
+ /**
+ * Test that a bug in skipRawBytes() has been fixed: if the skip skips
+ * exactly up to a limit, this should not break things.
+ */
+ public void testSkipRawBytesBug() throws Exception {
+ byte[] rawBytes = new byte[] { 1, 2 };
+ CodedInputStream input = CodedInputStream.newInstance(rawBytes);
+
+ int limit = input.pushLimit(1);
+ input.skipRawBytes(1);
+ input.popLimit(limit);
+ assertEquals(2, input.readRawByte());
+ }
+
public void testReadHugeBlob() throws Exception {
// Allocate and initialize a 1MB blob.
byte[] blob = new byte[1 << 20];
@@ -392,6 +429,30 @@ public class CodedInputStreamTest extends TestCase {
}
}
+ public void testResetSizeCounter() throws Exception {
+ CodedInputStream input = CodedInputStream.newInstance(
+ new SmallBlockInputStream(new byte[256], 8));
+ input.setSizeLimit(16);
+ input.readRawBytes(16);
+
+ try {
+ input.readRawByte();
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
+
+ input.resetSizeCounter();
+ input.readRawByte(); // No exception thrown.
+
+ try {
+ input.readRawBytes(16); // Hits limit again.
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
+ }
+
/**
* Tests that if we read an string that contains invalid UTF-8, no exception
* is thrown. Instead, the invalid bytes are replaced with the Unicode
diff --git a/java/src/test/java/com/google/protobuf/DynamicMessageTest.java b/java/src/test/java/com/google/protobuf/DynamicMessageTest.java
index fc53c531..120b0f13 100644
--- a/java/src/test/java/com/google/protobuf/DynamicMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/DynamicMessageTest.java
@@ -61,6 +61,18 @@ public class DynamicMessageTest extends TestCase {
reflectionTester.assertAllFieldsSetViaReflection(message);
}
+ public void testDoubleBuildError() throws Exception {
+ Message.Builder builder =
+ DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
+ builder.build();
+ try {
+ builder.build();
+ fail("Should have thrown exception.");
+ } catch (IllegalStateException e) {
+ // Success.
+ }
+ }
+
public void testDynamicMessageSettersRejectNull() throws Exception {
Message.Builder builder =
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 04ba769e..58d82193 100644
--- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -71,6 +71,17 @@ public class GeneratedMessageTest extends TestCase {
TestUtil.assertAllFieldsSet(message);
}
+ public void testDoubleBuildError() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.build();
+ try {
+ builder.build();
+ fail("Should have thrown exception.");
+ } catch (IllegalStateException e) {
+ // Success.
+ }
+ }
+
public void testSettersRejectNull() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/src/test/java/com/google/protobuf/ServiceTest.java
index 6a747f48..d8523ea3 100644
--- a/java/src/test/java/com/google/protobuf/ServiceTest.java
+++ b/java/src/test/java/com/google/protobuf/ServiceTest.java
@@ -30,6 +30,10 @@
package com.google.protobuf;
+import com.google.protobuf.Descriptors.MethodDescriptor;
+import protobuf_unittest.MessageWithNoOuter;
+import protobuf_unittest.ServiceWithNoOuter;
+import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestService;
import protobuf_unittest.UnittestProto.FooRequest;
import protobuf_unittest.UnittestProto.FooResponse;
@@ -56,6 +60,7 @@ public class ServiceTest extends TestCase {
private final Descriptors.MethodDescriptor barDescriptor =
TestService.getDescriptor().getMethods().get(1);
+ @Override
protected void setUp() throws Exception {
super.setUp();
control = EasyMock.createStrictControl();
@@ -127,6 +132,94 @@ public class ServiceTest extends TestCase {
control.verify();
}
+ /** Tests generated blocking stubs. */
+ public void testBlockingStub() throws Exception {
+ FooRequest fooRequest = FooRequest.newBuilder().build();
+ BarRequest barRequest = BarRequest.newBuilder().build();
+ BlockingRpcChannel mockChannel =
+ control.createMock(BlockingRpcChannel.class);
+ TestService.BlockingInterface stub =
+ TestService.newBlockingStub(mockChannel);
+
+ FooResponse fooResponse = FooResponse.newBuilder().build();
+ BarResponse barResponse = BarResponse.newBuilder().build();
+
+ EasyMock.expect(mockChannel.callBlockingMethod(
+ EasyMock.same(fooDescriptor),
+ EasyMock.same(mockController),
+ EasyMock.same(fooRequest),
+ EasyMock.same(FooResponse.getDefaultInstance()))).andReturn(fooResponse);
+ EasyMock.expect(mockChannel.callBlockingMethod(
+ EasyMock.same(barDescriptor),
+ EasyMock.same(mockController),
+ EasyMock.same(barRequest),
+ EasyMock.same(BarResponse.getDefaultInstance()))).andReturn(barResponse);
+ control.replay();
+
+ assertSame(fooResponse, stub.foo(mockController, fooRequest));
+ assertSame(barResponse, stub.bar(mockController, barRequest));
+ control.verify();
+ }
+
+ public void testNewReflectiveService() {
+ ServiceWithNoOuter.Interface impl =
+ control.createMock(ServiceWithNoOuter.Interface.class);
+ RpcController controller = control.createMock(RpcController.class);
+ Service service = ServiceWithNoOuter.newReflectiveService(impl);
+
+ MethodDescriptor fooMethod =
+ ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
+ MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
+ RpcCallback<Message> callback = new RpcCallback<Message>() {
+ public void run(Message parameter) {
+ // No reason this should be run.
+ fail();
+ }
+ };
+ RpcCallback<TestAllTypes> specializedCallback =
+ RpcUtil.specializeCallback(callback);
+
+ impl.foo(EasyMock.same(controller), EasyMock.same(request),
+ EasyMock.same(specializedCallback));
+ EasyMock.expectLastCall();
+
+ control.replay();
+
+ service.callMethod(fooMethod, controller, request, callback);
+
+ control.verify();
+ }
+
+ public void testNewReflectiveBlockingService() throws ServiceException {
+ ServiceWithNoOuter.BlockingInterface impl =
+ control.createMock(ServiceWithNoOuter.BlockingInterface.class);
+ RpcController controller = control.createMock(RpcController.class);
+ BlockingService service =
+ ServiceWithNoOuter.newReflectiveBlockingService(impl);
+
+ MethodDescriptor fooMethod =
+ ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
+ MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
+ RpcCallback<Message> callback = new RpcCallback<Message>() {
+ public void run(Message parameter) {
+ // No reason this should be run.
+ fail();
+ }
+ };
+
+ TestAllTypes expectedResponse = TestAllTypes.getDefaultInstance();
+ EasyMock.expect(impl.foo(EasyMock.same(controller), EasyMock.same(request)))
+ .andReturn(expectedResponse);
+
+ control.replay();
+
+ Message response =
+ service.callBlockingMethod(fooMethod, controller, request);
+ assertEquals(expectedResponse, response);
+
+ control.verify();
+ }
+
// =================================================================
/**
@@ -135,7 +228,7 @@ public class ServiceTest extends TestCase {
* In other words, c wraps the given callback.
*/
private <Type extends Message> RpcCallback<Type> wrapsCallback(
- MockCallback callback) {
+ MockCallback<?> callback) {
EasyMock.reportMatcher(new WrapsCallback(callback));
return null;
}
@@ -153,9 +246,9 @@ public class ServiceTest extends TestCase {
/** Implementation of the wrapsCallback() argument matcher. */
private static class WrapsCallback implements IArgumentMatcher {
- private MockCallback callback;
+ private MockCallback<?> callback;
- public WrapsCallback(MockCallback callback) {
+ public WrapsCallback(MockCallback<?> callback) {
this.callback = callback;
}
diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
index 212b381f..ea088b32 100644
--- a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
+++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
@@ -31,13 +31,13 @@
package com.google.protobuf;
import protobuf_unittest.UnittestProto;
-import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
-import protobuf_unittest.UnittestProto.
- TestEmptyMessageWithExtensions;
+import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions;
import junit.framework.TestCase;
+
import java.util.Arrays;
import java.util.Map;
@@ -341,4 +341,97 @@ public class UnknownFieldSetTest extends TestCase {
assertEquals(1, field.getVarintList().size());
assertEquals(0x7FFFFFFFFFFFFFFFL, (long)field.getVarintList().get(0));
}
+
+ public void testEqualsAndHashCode() {
+ UnknownFieldSet.Field fixed32Field =
+ UnknownFieldSet.Field.newBuilder()
+ .addFixed32(1)
+ .build();
+ UnknownFieldSet.Field fixed64Field =
+ UnknownFieldSet.Field.newBuilder()
+ .addFixed64(1)
+ .build();
+ UnknownFieldSet.Field varIntField =
+ UnknownFieldSet.Field.newBuilder()
+ .addVarint(1)
+ .build();
+ UnknownFieldSet.Field lengthDelimitedField =
+ UnknownFieldSet.Field.newBuilder()
+ .addLengthDelimited(ByteString.EMPTY)
+ .build();
+ UnknownFieldSet.Field groupField =
+ UnknownFieldSet.Field.newBuilder()
+ .addGroup(unknownFields)
+ .build();
+
+ UnknownFieldSet a =
+ UnknownFieldSet.newBuilder()
+ .addField(1, fixed32Field)
+ .build();
+ UnknownFieldSet b =
+ UnknownFieldSet.newBuilder()
+ .addField(1, fixed64Field)
+ .build();
+ UnknownFieldSet c =
+ UnknownFieldSet.newBuilder()
+ .addField(1, varIntField)
+ .build();
+ UnknownFieldSet d =
+ UnknownFieldSet.newBuilder()
+ .addField(1, lengthDelimitedField)
+ .build();
+ UnknownFieldSet e =
+ UnknownFieldSet.newBuilder()
+ .addField(1, groupField)
+ .build();
+
+ checkEqualsIsConsistent(a);
+ checkEqualsIsConsistent(b);
+ checkEqualsIsConsistent(c);
+ checkEqualsIsConsistent(d);
+ checkEqualsIsConsistent(e);
+
+ checkNotEqual(a, b);
+ checkNotEqual(a, c);
+ checkNotEqual(a, d);
+ checkNotEqual(a, e);
+ checkNotEqual(b, c);
+ checkNotEqual(b, d);
+ checkNotEqual(b, e);
+ checkNotEqual(c, d);
+ checkNotEqual(c, e);
+ checkNotEqual(d, e);
+ }
+
+ /**
+ * Asserts that the given field sets are not equal and have different
+ * hash codes.
+ *
+ * @warning It's valid for non-equal objects to have the same hash code, so
+ * this test is stricter than it needs to be. However, this should happen
+ * relatively rarely.
+ */
+ private void checkNotEqual(UnknownFieldSet s1, UnknownFieldSet s2) {
+ String equalsError = String.format("%s should not be equal to %s", s1, s2);
+ assertFalse(equalsError, s1.equals(s2));
+ assertFalse(equalsError, s2.equals(s1));
+
+ assertFalse(
+ String.format("%s should have a different hash code from %s", s1, s2),
+ s1.hashCode() == s2.hashCode());
+ }
+
+ /**
+ * Asserts that the given field sets are equal and have identical hash codes.
+ */
+ private void checkEqualsIsConsistent(UnknownFieldSet set) {
+ // Object should be equal to itself.
+ assertEquals(set, set);
+
+ // Object should be equal to a copy of itself.
+ UnknownFieldSet copy = UnknownFieldSet.newBuilder(set).build();
+ assertEquals(set, copy);
+ assertEquals(copy, set);
+ assertEquals(set.hashCode(), copy.hashCode());
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/src/test/java/com/google/protobuf/WireFormatTest.java
index 48453faf..4afefdb6 100644
--- a/java/src/test/java/com/google/protobuf/WireFormatTest.java
+++ b/java/src/test/java/com/google/protobuf/WireFormatTest.java
@@ -31,6 +31,10 @@
package com.google.protobuf;
import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
@@ -130,6 +134,22 @@ public class WireFormatTest extends TestCase {
TestUtil.getAllExtensionsSet().getSerializedSize());
}
+ public void testSerializeDelimited() throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ TestUtil.getAllSet().writeDelimitedTo(output);
+ output.write(12);
+ TestUtil.getPackedSet().writeDelimitedTo(output);
+ output.write(34);
+
+ ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+
+ TestUtil.assertAllFieldsSet(TestAllTypes.parseDelimitedFrom(input));
+ assertEquals(12, input.read());
+ TestUtil.assertPackedFieldsSet(TestPackedTypes.parseDelimitedFrom(input));
+ assertEquals(34, input.read());
+ assertEquals(-1, input.read());
+ }
+
private void assertFieldsInOrder(ByteString data) throws Exception {
CodedInputStream input = data.newCodedInput();
int previousTag = 0;