diff --git a/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java b/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java index f804900..fd58beb 100644 --- a/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java +++ b/dna-common/src/main/java/org/jboss/dna/common/util/Base64.java @@ -671,6 +671,75 @@ public class Base64 { } + /** + * Encodes content of the supplied InputStream into Base64 notation. Does not GZip-compress data. + * + * @param source The data to convert + * @return the encoded bytes + */ + public static String encode( java.io.InputStream source ) { + return encode(source, NO_OPTIONS); + } + + /** + * Encodes the content of the supplied InputStream into Base64 notation. + *
+ * Valid options: + * + *
+ * GZIP: gzip-compresses object before encoding it. + * DONT_BREAK_LINES: don't break lines at 76 characters + * <i>Note: Technically, this makes your encoding non-compliant.</i> + *+ *
+ * Example: encodeBytes( myData, Base64.GZIP )
or
+ *
+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )
+ *
+ * @param source The data to convert
+ * @param options Specified options- the alphabet type is pulled from this (standard, url-safe, ordered)
+ * @return the encoded bytes
+ * @see Base64#GZIP
+ * @see Base64#DONT_BREAK_LINES
+ */
+ public static String encode( java.io.InputStream source,
+ int options ) {
+ CheckArg.isNotNull(source, "source");
+ java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
+ Base64.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options);
+ BufferedInputStream input = new BufferedInputStream(source);
+ java.io.OutputStream output = b64os;
+
+ boolean error = false;
+ try {
+ if ((options & GZIP) == GZIP) {
+ output = new java.util.zip.GZIPOutputStream(output);
+ }
+ int numRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((numRead = input.read(buffer)) > -1) {
+ output.write(buffer, 0, numRead);
+ }
+ output.close();
+ } catch (IOException e) {
+ error = true;
+ throw new SystemFailureException(e); // error using reading from byte array!
+ } finally {
+ try {
+ input.close();
+ } catch (IOException e) {
+ if (!error) new SystemFailureException(e); // error closing input stream
+ }
+ }
+
+ // Return value according to relevant encoding.
+ try {
+ return new String(baos.toByteArray(), PREFERRED_ENCODING);
+ } catch (java.io.UnsupportedEncodingException uue) {
+ return new String(baos.toByteArray());
+ }
+ }
+
/* ******** D E C O D I N G M E T H O D S ******** */
/**
diff --git a/dna-common/src/test/java/org/jboss/dna/common/util/Base64Test.java b/dna-common/src/test/java/org/jboss/dna/common/util/Base64Test.java
index 097c680..d345b05 100644
--- a/dna-common/src/test/java/org/jboss/dna/common/util/Base64Test.java
+++ b/dna-common/src/test/java/org/jboss/dna/common/util/Base64Test.java
@@ -26,6 +26,9 @@ package org.jboss.dna.common.util;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import org.junit.Test;
/**
@@ -68,6 +71,28 @@ public class Base64Test {
System.out.println();
}
+ @Test
+ public void shouldEncodeStringValue() throws UnsupportedEncodingException {
+ String actualValue = "propertyValue";
+ String encoded = Base64.encodeBytes(actualValue.getBytes("UTF-8"));
+ byte[] decoded = Base64.decode(encoded);
+ String decodedValue = new String(decoded, "UTF-8");
+ assertThat(decodedValue, is(actualValue));
+ }
+
+ @Test
+ public void shouldEncodeStreamableValue() {
+ String actualValue = "propertyValue";
+ byte[] actualBytes = actualValue.getBytes();
+ InputStream actualStream = new ByteArrayInputStream(actualBytes);
+ String encoded = Base64.encode(actualStream);
+ String encoded2 = Base64.encodeBytes(actualBytes);
+ assertThat(encoded, is(encoded2));
+ byte[] decoded = Base64.decode(encoded);
+ String decodedValue = new String(decoded);
+ assertThat(decodedValue, is(actualValue));
+ }
+
@Test( expected = NullPointerException.class )
public void testEncodeNullByteArray() {
Base64.encodeBytes(null);
diff --git a/extensions/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java b/extensions/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java
index 89961fc..d4bf4d1 100644
--- a/extensions/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java
+++ b/extensions/dna-web-jcr-rest-war/src/test/java/org/jboss/dna/web/jcr/rest/JcrResourcesTest.java
@@ -41,12 +41,11 @@ import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
+import com.sun.org.apache.xml.internal.security.utils.Base64;
/**
- * Test of the DNA JCR REST resource. Note that this test case uses a very low-level API to construct
- * requests and deconstruct the responses. Users are encouraged to use a higher-level library to communicate
- * with the REST server (e.g., Apache HTTP Commons).
- *
+ * Test of the DNA JCR REST resource. Note that this test case uses a very low-level API to construct requests and deconstruct the
+ * responses. Users are encouraged to use a higher-level library to communicate with the REST server (e.g., Apache HTTP Commons).
*/
public class JcrResourcesTest {
@@ -57,17 +56,17 @@ public class JcrResourcesTest {
public void beforeEach() {
// Configured in pom
- final String login ="dnauser";
- final String password ="password";
+ final String login = "dnauser";
+ final String password = "password";
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication (login, password.toCharArray());
+ return new PasswordAuthentication(login, password.toCharArray());
}
});
}
-
+
private String getResponseFor( HttpURLConnection connection ) throws IOException {
StringBuffer buff = new StringBuffer();
@@ -84,13 +83,13 @@ public class JcrResourcesTest {
@Test
public void shouldNotServeContentToUnauthorizedUser() throws Exception {
- final String login ="dnauser";
- final String password ="invalidpassword";
+ final String login = "dnauser";
+ final String password = "invalidpassword";
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication (login, password.toCharArray());
+ return new PasswordAuthentication(login, password.toCharArray());
}
});
@@ -110,13 +109,13 @@ public class JcrResourcesTest {
public void shouldNotServeContentToUserWithoutConnectRole() throws Exception {
// Configured in pom
- final String login ="unauthorizeduser";
- final String password ="password";
+ final String login = "unauthorizeduser";
+ final String password = "password";
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
- return new PasswordAuthentication (login, password.toCharArray());
+ return new PasswordAuthentication(login, password.toCharArray());
}
});
@@ -323,7 +322,7 @@ public class JcrResourcesTest {
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
String body = getResponseFor(connection);
- assertThat(body, is("\"dna:system\""));
+ assertThat(body, is("{\"jcr:primaryType\":\"dna:system\"}"));
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
}
@@ -339,7 +338,7 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]}}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
@@ -355,7 +354,7 @@ public class JcrResourcesTest {
assertThat(values.length(), is(2));
assertThat(values.getString(0), is("value1"));
assertThat(values.getString(1), is("value2"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
}
@@ -371,15 +370,15 @@ public class JcrResourcesTest {
String payload = "{}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
-
+
JSONObject properties = body.getJSONObject("properties");
assertThat(properties, is(notNullValue()));
assertThat(properties.length(), is(1));
assertThat(properties.getString("jcr:primaryType"), is("nt:unstructured"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
}
@@ -395,10 +394,10 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:mixinTypes\": \"mix:referenceable\"}}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
-
+
JSONObject properties = body.getJSONObject("properties");
assertThat(properties, is(notNullValue()));
assertThat(properties.length(), is(3));
@@ -409,7 +408,7 @@ public class JcrResourcesTest {
assertThat(values, is(notNullValue()));
assertThat(values.length(), is(1));
assertThat(values.getString(0), is("mix:referenceable"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
@@ -423,7 +422,7 @@ public class JcrResourcesTest {
body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
-
+
properties = body.getJSONObject("properties");
assertThat(properties, is(notNullValue()));
assertThat(properties.length(), is(3));
@@ -434,10 +433,10 @@ public class JcrResourcesTest {
assertThat(values, is(notNullValue()));
assertThat(values.length(), is(1));
assertThat(values.getString(0), is("mix:referenceable"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
-
+
}
@Test
@@ -448,7 +447,7 @@ public class JcrResourcesTest {
connection.setDoOutput(true);
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND));
connection.disconnect();
@@ -465,7 +464,7 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"invalidType\", \"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]}}";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
connection.disconnect();
@@ -491,7 +490,7 @@ public class JcrResourcesTest {
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]},"
- + " \"children\": { \"childNode\" : { \"properties\": {\"nestedProperty\": \"nestedValue\"}}}}";
+ + " \"children\": { \"childNode\" : { \"properties\": {\"nestedProperty\": \"nestedValue\"}}}}";
connection.getOutputStream().write(payload.getBytes());
@@ -508,7 +507,7 @@ public class JcrResourcesTest {
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(2));
-
+
JSONObject properties = body.getJSONObject("properties");
assertThat(properties, is(notNullValue()));
assertThat(properties.length(), is(3));
@@ -521,22 +520,22 @@ public class JcrResourcesTest {
assertThat(values.length(), is(2));
assertThat(values.getString(0), is("value1"));
assertThat(values.getString(1), is("value2"));
-
+
JSONObject children = body.getJSONObject("children");
assertThat(children, is(notNullValue()));
assertThat(children.length(), is(1));
-
+
JSONObject child = children.getJSONObject("childNode");
assertThat(child, is(notNullValue()));
assertThat(child.length(), is(1));
-
+
properties = child.getJSONObject("properties");
assertThat(properties, is(notNullValue()));
assertThat(properties.length(), is(2));
// Parent primary type is nt:unstructured, so this should default to nt:unstructured primary type
assertThat(properties.getString("jcr:primaryType"), is("nt:unstructured"));
assertThat(properties.getString("nestedProperty"), is("nestedValue"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
@@ -552,9 +551,9 @@ public class JcrResourcesTest {
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]},"
- + " \"children\": { \"childNode\" : { \"properties\": {\"jcr:primaryType\": \"invalidType\"}}}}";
+ + " \"children\": { \"childNode\" : { \"properties\": {\"jcr:primaryType\": \"invalidType\"}}}}";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
connection.disconnect();
@@ -565,13 +564,13 @@ public class JcrResourcesTest {
connection.setDoOutput(true);
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND));
connection.disconnect();
-
+
}
- @Test
+ @Test
public void shouldNotDeleteNonExistentItem() throws Exception {
URL postUrl = new URL(SERVER_URL + "/dna%3arepository/default/items/invalidItemForDelete");
HttpURLConnection connection = (HttpURLConnection)postUrl.openConnection();
@@ -579,12 +578,12 @@ public class JcrResourcesTest {
connection.setDoOutput(true);
connection.setRequestMethod("DELETE");
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND));
connection.disconnect();
}
- @Test
+ @Test
public void shouldDeleteExtantNode() throws Exception {
// Create the node
@@ -597,7 +596,7 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]}}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
@@ -613,7 +612,7 @@ public class JcrResourcesTest {
assertThat(values.length(), is(2));
assertThat(values.getString(0), is("value1"));
assertThat(values.getString(1), is("value2"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
@@ -651,7 +650,7 @@ public class JcrResourcesTest {
connection.disconnect();
}
- @Test
+ @Test
public void shouldDeleteExtantProperty() throws Exception {
URL postUrl = new URL(SERVER_URL + "/dna%3arepository/default/items/propertyForDeletion");
HttpURLConnection connection = (HttpURLConnection)postUrl.openConnection();
@@ -662,10 +661,10 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]}}";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
-
+
// Confirm that it exists
postUrl = new URL(SERVER_URL + "/dna%3arepository/default/items/propertyForDeletion");
connection = (HttpURLConnection)postUrl.openConnection();
@@ -723,7 +722,7 @@ public class JcrResourcesTest {
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
-
+
}
@Test
@@ -738,7 +737,7 @@ public class JcrResourcesTest {
String payload = "{ \"firstProperty\": \"someValue\" }";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND));
connection.disconnect();
}
@@ -754,7 +753,7 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty\": \"testValue\" }}";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
@@ -765,9 +764,9 @@ public class JcrResourcesTest {
connection.setRequestMethod("PUT");
connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
- payload = "\"someOtherValue\"";
+ payload = "{\"testProperty\":\"someOtherValue\"}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
@@ -776,14 +775,82 @@ public class JcrResourcesTest {
assertThat(properties.length(), is(2));
assertThat(properties.getString("jcr:primaryType"), is("nt:unstructured"));
assertThat(properties.getString("testProperty"), is("someOtherValue"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
-
+
}
@Test
- public void shouldNotBeAbleToPutPropertiesToNode() throws Exception {
+ public void shouldBeAbleToPutBinaryValueToProperty() throws Exception {
+ URL postUrl = new URL(SERVER_URL + "/dna%3arepository/default/items/nodeForPutBinaryProperty");
+ HttpURLConnection connection = (HttpURLConnection)postUrl.openConnection();
+
+ connection.setDoOutput(true);
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
+
+ // Base64-encode a value ...
+ String encodedValue = Base64.encode("propertyValue".getBytes("UTF-8"));
+
+ String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\", \"testProperty/base64/\": \""
+ + encodedValue + "\" }}";
+ connection.getOutputStream().write(payload.getBytes());
+
+ assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
+ connection.disconnect();
+
+ URL putUrl = new URL(SERVER_URL + "/dna%3arepository/default/items/nodeForPutBinaryProperty/testProperty");
+ connection = (HttpURLConnection)putUrl.openConnection();
+
+ connection.setDoOutput(true);
+ connection.setRequestMethod("PUT");
+ connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
+
+ String otherValue = "someOtherValue";
+ payload = "{\"testProperty/base64/\":\"" + Base64.encode(otherValue.getBytes("UTF-8")) + "\"}";
+ connection.getOutputStream().write(payload.getBytes());
+
+ JSONObject body = new JSONObject(getResponseFor(connection));
+ assertThat(body.length(), is(1));
+
+ JSONObject properties = body.getJSONObject("properties");
+ assertThat(properties, is(notNullValue()));
+ assertThat(properties.length(), is(2));
+ assertThat(properties.getString("jcr:primaryType"), is("nt:unstructured"));
+ String responseEncodedValue = properties.getString("testProperty/base64/");
+ String decodedValue = new String(Base64.decode(responseEncodedValue), "UTF-8");
+ assertThat(decodedValue, is(otherValue));
+
+ assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
+ connection.disconnect();
+
+ // Try putting a non-binary value ...
+ connection = (HttpURLConnection)putUrl.openConnection();
+
+ connection.setDoOutput(true);
+ connection.setRequestMethod("PUT");
+ connection.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON);
+
+ String anotherValue = "yetAnotherValue";
+ payload = "{\"testProperty\":\"" + anotherValue + "\"}";
+ connection.getOutputStream().write(payload.getBytes());
+
+ body = new JSONObject(getResponseFor(connection));
+ assertThat(body.length(), is(1));
+
+ properties = body.getJSONObject("properties");
+ assertThat(properties, is(notNullValue()));
+ assertThat(properties.length(), is(2));
+ assertThat(properties.getString("jcr:primaryType"), is("nt:unstructured"));
+ assertThat(properties.getString("testProperty"), is(anotherValue));
+
+ assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
+ connection.disconnect();
+ }
+
+ @Test
+ public void shouldBeAbleToPutPropertiesToNode() throws Exception {
URL postUrl = new URL(SERVER_URL + "/dna%3arepository/default/items/nodeForPutProperties");
HttpURLConnection connection = (HttpURLConnection)postUrl.openConnection();
@@ -794,7 +861,7 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\" }}";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
@@ -807,7 +874,7 @@ public class JcrResourcesTest {
payload = "{\"testProperty\": \"testValue\", \"multiValuedProperty\": [\"value1\", \"value2\"]}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
@@ -823,10 +890,10 @@ public class JcrResourcesTest {
assertThat(values.length(), is(2));
assertThat(values.getString(0), is("value1"));
assertThat(values.getString(1), is("value2"));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
-
+
}
@Test
@@ -841,7 +908,7 @@ public class JcrResourcesTest {
String payload = "{ \"properties\": {\"jcr:primaryType\": \"nt:unstructured\" }}";
connection.getOutputStream().write(payload.getBytes());
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_CREATED));
connection.disconnect();
@@ -853,7 +920,7 @@ public class JcrResourcesTest {
payload = "{\"jcr:mixinTypes\": \"mix:referenceable\"}";
connection.getOutputStream().write(payload.getBytes());
-
+
JSONObject body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
@@ -867,7 +934,7 @@ public class JcrResourcesTest {
assertThat(mixinTypes.length(), is(1));
assertThat(mixinTypes.getString(0), is("mix:referenceable"));
assertThat(properties.getString("jcr:uuid"), is(notNullValue()));
-
+
assertThat(connection.getResponseCode(), is(HttpURLConnection.HTTP_OK));
connection.disconnect();
@@ -879,7 +946,7 @@ public class JcrResourcesTest {
payload = "{\"jcr:mixinTypes\": []}";
connection.getOutputStream().write(payload.getBytes());
-
+
body = new JSONObject(getResponseFor(connection));
assertThat(body.length(), is(1));
diff --git a/extensions/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java b/extensions/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java
index e446589..f9c08a9 100644
--- a/extensions/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java
+++ b/extensions/dna-web-jcr-rest/src/main/java/org/jboss/dna/web/jcr/rest/JcrResources.java
@@ -23,9 +23,10 @@
*/
package org.jboss.dna.web.jcr.rest;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -38,9 +39,11 @@ import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
+import javax.jcr.ValueFactory;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
import javax.servlet.http.HttpServletRequest;
@@ -64,6 +67,7 @@ import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.jboss.dna.common.text.UrlEncoder;
+import org.jboss.dna.common.util.Base64;
import org.jboss.dna.web.jcr.rest.model.RepositoryEntry;
import org.jboss.dna.web.jcr.rest.model.WorkspaceEntry;
import org.jboss.resteasy.spi.NotFoundException;
@@ -99,6 +103,55 @@ import org.jboss.resteasy.spi.UnauthorizedException;
*
+ * There are several ways to transfer binary property values, but all involve encoding the binary value into ASCII characters + * using a {@link Base64} notation and denoting this by adding annotating the property name with a suffix defining the type of + * encoding. Currently, only "base64" encoding is supported. + *
+ *+ * For example, if the "jcr:data" property contains a single binary value of "propertyValue", then the JSON object representing + * that property will be: + * + *
+ * "jcr:data/base64/" : "cHJvcGVydHlWYWx1ZQ==" + *+ * + * Likewise, if the "jcr:data" property contains two binary values each being "propertyValue", then the JSON object representing + * that property will be: + * + *
+ * "jcr:data/base64/" : [ "cHJvcGVydHlWYWx1ZQ==", "cHJvcGVydHlWYWx1ZQ==" ] + *+ * + * Note that JCR 1.0.1 does not allow property names to and with a '/' character (among others), while JCR 2.0 does not allow + * property names to contain an unescaped or unencoded '/' character. Therefore, the "/{encoding}/" suffix can never appear in a + * valid JCR property name, and will always identify an encoded property. + * + *
+ * Here are the details: + *
GET /resources/{repositoryName}/item/{pathToNode}
obtains the JSON object representing the
+ * node, and each property is represented as a nested JSON object where the name is the property name and the value(s) are
+ * represented as either a single string value or an array of string values. If the property has a binary value, then the property
+ * name is appended with "/base64/" and the string representation of each value is encoded in Base64.GET /resources/{repositoryName}/item/{pathToProperty}
allows only the value(s) for the
+ * one property to be included in the response. If any of the values is a binary value, then all of the values will be
+ * encoded in Base64.PUT /resources/{repositoryName}/item/{pathToProperty}
allows setting the property to a
+ * single value, and only that value needs to be included in the body of the request. If the value is binary, the value
+ * must be {@link Base64 encoded} by the client and the "Content-Transfer-Encoding" header must be set to "base64" (case
+ * does not matter). When the request is received, the value is decoded before the property value is updated on the node.POST /resources/{repositoryName}/item/{pathToNode}
requires a request that is structured
+ * in the same way as the response from getting a node: the resulting JSON object represents the node, with nested JSON objects
+ * for the properties and children. If any property of the new node has a binary value, then the name of the property must
+ * be appended with "/base64/" and the string representation of each value are to be encoded in Base64.PUT /resources/{repositoryName}/item/{pathToNode}
requires a request that is structured
+ * in the same way as the response from getting or posting a node: the resulting JSON object represents the node, with nested JSON
+ * objects for the properties and children. If any property of the new node has a binary value, then the name of the property
+ * must be appended with "/base64/" and the string representation of each value are to be encoded in Base64.+ * Note that if any of the values are binary, then all values will be first encoded as {@link Base64} string values. + * However, if no values are binary, then all values will simply be the {@link Value#getString() string} representation of the + * value. + *
* * @param property the property to be encoded * @return the JSON-encoded version of the property + * @throws JSONException if there is an error encoding the node * @throws RepositoryException if an error occurs accessing the property, its values, or its definition. * @see Property#getDefinition() * @see PropertyDefinition#isMultiple() */ - private String jsonFor( Property property ) throws RepositoryException { + private JSONObject jsonFor( Property property ) throws JSONException, RepositoryException { + boolean encoded = false; + Object valueObject = null; if (property.getDefinition().isMultiple()) { Value[] values = property.getValues(); + for (Value value : values) { + if (value.getType() == PropertyType.BINARY) { + encoded = true; + break; + } + } List