diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java
index c79123859c..7bb65ed241 100644
--- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java
+++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java
@@ -1,28 +1,28 @@
-/*
- * #%L
- * Alfresco Remote API
- * %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
- * %%
- * This file is part of the Alfresco software.
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail. Otherwise, the software is
- * provided under the following open source license terms:
- *
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Alfresco is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- * #L%
- */
+/*
+ * #%L
+ * Alfresco Remote API
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
package org.alfresco.rest.api.impl;
import org.alfresco.model.ApplicationModel;
@@ -140,9 +140,13 @@ import org.springframework.extensions.webscripts.servlet.FormData;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.math.BigInteger;
+import java.nio.charset.Charset;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1625,10 +1629,8 @@ public class NodesImpl implements Nodes
if (isContent)
{
- // add empty file
- ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
- setWriterContentType(writer, new ContentInfoWrapper(nodeInfo.getContent()), nodeRef, false);
- writer.putContent("");
+ // add empty file - note: currently will be set to default encoding only (UTF-8)
+ writeContent(nodeRef, nodeName, new ByteArrayInputStream("".getBytes()), false);
}
// eg. to create mandatory assoc(s)
@@ -2256,7 +2258,7 @@ public class NodesImpl implements Nodes
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
try
{
- writeContent(nodeRef, contentInfo, stream);
+ writeContent(nodeRef, fileName, stream, true);
if ((isVersioned) || (versionMajor != null) || (versionComment != null) )
{
@@ -2281,11 +2283,69 @@ public class NodesImpl implements Nodes
return getFolderOrDocumentFullInfo(nodeRef, null, null, parameters);
}
- private void writeContent(NodeRef nodeRef, BasicContentInfo contentInfo, InputStream stream)
+ private void writeContent(NodeRef nodeRef, String fileName, InputStream stream, boolean guessEncoding)
{
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
- setWriterContentType(writer, new ContentInfoWrapper(contentInfo), nodeRef, true);
- writer.putContent(stream);
+
+ String mimeType = mimetypeService.guessMimetype(fileName);
+ writer.setMimetype(mimeType);
+
+ InputStream is = null;
+
+ if (guessEncoding)
+ {
+ is = new BufferedInputStream(stream);
+ is.mark(1024);
+ writer.setEncoding(guessEncoding(is, mimeType, false));
+ try
+ {
+ is.reset();
+ }
+ catch (IOException ioe)
+ {
+ if (logger.isWarnEnabled())
+ {
+ logger.warn("Failed to reset stream after trying to guess encoding: " + ioe.getMessage());
+ }
+ }
+ }
+ else
+ {
+ is = stream;
+ }
+
+ writer.putContent(is);
+ }
+
+ private String guessEncoding(InputStream in, String mimeType, boolean close)
+ {
+ String encoding = "UTF-8";
+ try
+ {
+ if (in != null)
+ {
+ Charset charset = mimetypeService.getContentCharsetFinder().getCharset(in, mimeType);
+ encoding = charset.name();
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (close && (in != null))
+ {
+ in.close();
+ }
+ }
+ catch (IOException ioe)
+ {
+ if (logger.isWarnEnabled())
+ {
+ logger.warn("Failed to close stream after trying to guess encoding: " + ioe.getMessage());
+ }
+ }
+ }
+ return encoding;
}
protected void createVersion(NodeRef nodeRef, boolean isVersioned, VersionType versionType, String reason)
@@ -2308,34 +2368,6 @@ public class NodesImpl implements Nodes
}
}
- private void setWriterContentType(ContentWriter writer, ContentInfoWrapper contentInfo, NodeRef nodeRef, boolean guessEncodingIfNull)
- {
- String mimeType = contentInfo.mimeType;
- // Manage MimeType
- if ((mimeType == null) || mimeType.equals(DEFAULT_MIMETYPE))
- {
- // the mimeType was not provided (or was the default binary mimeType) via the contentInfo, so try to guess
- final String fileName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
- mimeType = mimetypeService.guessMimetype(fileName);
- }
- writer.setMimetype(mimeType);
-
- // Manage Encoding
- if (contentInfo.encoding == null)
- {
- if (guessEncodingIfNull)
- {
- // the encoding was not provided, so try to guess
- writer.guessEncoding();
- }
- }
- else
- {
- writer.setEncoding(contentInfo.encoding);
- }
- }
-
-
@Override
public Node upload(String parentFolderNodeId, FormData formData, Parameters parameters)
{
@@ -2543,19 +2575,20 @@ public class NodesImpl implements Nodes
{
nodeType = ContentModel.TYPE_CONTENT;
}
- NodeRef newFile = createNodeImpl(parentNodeRef, fileName, nodeType, props, assocTypeQName);
+
+ NodeRef nodeRef = createNodeImpl(parentNodeRef, fileName, nodeType, props, assocTypeQName);
// Write content
- write(newFile, content);
+ writeContent(nodeRef, fileName, content.getInputStream(), true);
// Ensure the file is versionable (autoVersion = true, autoVersionProps = false)
- ensureVersioningEnabled(newFile, true, false);
+ ensureVersioningEnabled(nodeRef, true, false);
// Extract the metadata
- extractMetadata(newFile);
+ extractMetadata(nodeRef);
// Create the response
- return getFolderOrDocumentFullInfo(newFile, parentNodeRef, nodeType, params);
+ return getFolderOrDocumentFullInfo(nodeRef, parentNodeRef, nodeType, params);
}
private String getStringOrNull(String value)
@@ -2652,23 +2685,6 @@ public class NodesImpl implements Nodes
}
}
-
-
- /**
- * Writes the content to the repository.
- *
- * @param nodeRef the reference to the node having a content property
- * @param content the content
- */
- protected void write(NodeRef nodeRef, Content content)
- {
- ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
- // Per RA-637 & RA-885 requirement the mimeType provided by the client takes precedence, however,
- // if the mimeType is null (or default binary mimeType) then it will be guessed.
- setWriterContentType(writer, new ContentInfoWrapper(content), nodeRef, true);
- writer.putContent(content.getInputStream());
- }
-
/**
* Ensures the given node has the {@code cm:versionable} aspect applied to it, and
* that it has the initial version in the version store.
@@ -2806,11 +2822,22 @@ public class NodesImpl implements Nodes
/**
* @author Jamal Kaabi-Mofrad
*/
- private static class ContentInfoWrapper
+ /*
+ private static class ContentInfoWrapper implements BasicContentInfo
{
private String mimeType;
private String encoding;
+ public String getEncoding()
+ {
+ return encoding;
+ }
+
+ public String getMimeType()
+ {
+ return mimeType;
+ }
+
ContentInfoWrapper(BasicContentInfo basicContentInfo)
{
if (basicContentInfo != null)
@@ -2851,5 +2878,6 @@ public class NodesImpl implements Nodes
}
}
}
+ */
}
diff --git a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java
index de91d08b98..25902cb9bb 100644
--- a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java
+++ b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java
@@ -39,9 +39,6 @@ import org.alfresco.repo.content.ContentLimitProvider.SimpleFixedLimitProvider;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.node.archive.NodeArchiveService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.tenant.TenantAdminService;
-import org.alfresco.repo.tenant.TenantService;
-import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.NodeTarget;
@@ -346,10 +343,10 @@ public class NodeApiTest extends AbstractBaseApiTest
String folder2 = "folder " + System.currentTimeMillis() + " 2";
String folder2_Id = createFolder(user1, myNodeId, folder2, props).getId();
- String contentF2 = "content" + System.currentTimeMillis() + " in folder 2";
+ String contentF2 = "content" + System.currentTimeMillis() + " in folder 2.txt";
String contentF2_Id = createTextFile(user1, folder2_Id, contentF2, "The quick brown fox jumps over the lazy dog 2.").getId();
- String content1 = "content" + System.currentTimeMillis() + " 1";
+ String content1 = "content" + System.currentTimeMillis() + " 1.txt";
String content1_Id = createTextFile(user1, myNodeId, content1, "The quick brown fox jumps over the lazy dog.").getId();
props = new HashMap<>();
@@ -666,7 +663,7 @@ public class NodeApiTest extends AbstractBaseApiTest
String title = "test title";
Map docProps = new HashMap<>();
docProps.put("cm:title", title);
- String contentName = "content " + System.currentTimeMillis();
+ String contentName = "content " + System.currentTimeMillis() + ".txt";
String content1Id = createTextFile(user1, folderB_Id, contentName, "The quick brown fox jumps over the lazy dog.", "UTF-8", docProps).getId();
@@ -688,7 +685,7 @@ public class NodeApiTest extends AbstractBaseApiTest
ci.setMimeType("text/plain");
ci.setMimeTypeName("Plain Text");
ci.setSizeInBytes(44L);
- ci.setEncoding("UTF-8");
+ ci.setEncoding("ISO-8859-1");
d1.setContent(ci);
d1.setCreatedByUser(expectedUser);
@@ -949,7 +946,7 @@ public class NodeApiTest extends AbstractBaseApiTest
// User1 uploads a new file
reqBody = MultiPartBuilder.create()
- .setFileData(new FileData(fileName2, file2, MimetypeMap.MIMETYPE_TEXT_PLAIN, "windows-1252"))
+ .setFileData(new FileData(fileName2, file2))
.build();
response = post(getNodeChildrenUrl(user1Home.getId()), user1, reqBody.getBody(), null, reqBody.getContentType(), 201);
document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
@@ -958,14 +955,7 @@ public class NodeApiTest extends AbstractBaseApiTest
contentInfo = document.getContent();
assertNotNull(contentInfo);
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
- assertEquals("windows-1252", contentInfo.getEncoding());
-
- // Test invalid mimeType
- reqBody = MultiPartBuilder.create()
- .setFileData(new FileData(fileName2, file2, "*/invalidSubType", "ISO-8859-1"))
- .setAutoRename(true)
- .build();
- post(getNodeChildrenUrl(user1Home.getId()), user1, reqBody.getBody(), null, reqBody.getContentType(), 400);
+ assertEquals("ISO-8859-1", contentInfo.getEncoding());
// Test content size limit
final SimpleFixedLimitProvider limitProvider = applicationContext.getBean("defaultContentLimitProvider", SimpleFixedLimitProvider.class);
@@ -2754,27 +2744,29 @@ public class NodeApiTest extends AbstractBaseApiTest
assertNotNull(f1_nodeId);
Document doc = new Document();
- final String docName = "testdoc";
+ final String docName = "testdoc.txt";
doc.setName(docName);
doc.setNodeType(TYPE_CM_CONTENT);
doc.setProperties(Collections.singletonMap("cm:title", (Object)"test title"));
ContentInfo contentInfo = new ContentInfo();
- contentInfo.setMimeType(MimetypeMap.MIMETYPE_TEXT_PLAIN);
doc.setContent(contentInfo);
// create an empty file within F1 folder
response = post(getNodeChildrenUrl(f1_nodeId), user1, toJsonAsStringNonNull(doc), 201);
Document docResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+
assertEquals(docName, docResp.getName());
assertNotNull(docResp.getContent());
assertEquals(0, docResp.getContent().getSizeInBytes().intValue());
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, docResp.getContent().getMimeType());
+ // Default encoding
+ assertEquals("UTF-8", docResp.getContent().getEncoding());
// Update the empty node's content
String content = "The quick brown fox jumps over the lazy dog.";
ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes());
File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt");
- BinaryPayload payload = new BinaryPayload(txtFile, MimetypeMap.MIMETYPE_TEXT_PLAIN);
+ BinaryPayload payload = new BinaryPayload(txtFile);
// Try to update a folder!
putBinary(getNodeContentUrl(f1_nodeId), user1, payload, null, null, 400);
@@ -2804,11 +2796,10 @@ public class NodeApiTest extends AbstractBaseApiTest
contentInfo = docResp.getContent();
assertNotNull(contentInfo);
assertNotNull(contentInfo.getEncoding());
- // Default encoding
- assertEquals("UTF-8", contentInfo.getEncoding());
assertTrue(contentInfo.getSizeInBytes() > 0);
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
assertNotNull(contentInfo.getMimeTypeName());
+ assertEquals("ISO-8859-1", contentInfo.getEncoding());
// path is not part of the default response
assertNull(docResp.getPath());
@@ -2816,17 +2807,19 @@ public class NodeApiTest extends AbstractBaseApiTest
response = getSingle(url, user1, null, 200);
assertEquals(content, response.getResponse());
- // Update the node's content again. Also, change the mimeType and make the response to return path!
- File pdfFile = getResourceFile("quick.pdf");
- payload = new BinaryPayload(pdfFile, MimetypeMap.MIMETYPE_PDF, "ISO-8859-1");
+ // Update the node's content again. Also make the response return the path!
+ content = "The quick brown fox jumps over the lazy dog updated !";
+ inputStream = new ByteArrayInputStream(content.getBytes());
+ txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt");
+ payload = new BinaryPayload(txtFile);
response = putBinary(url + "?include=path", user1, payload, null, null, 200);
docResp = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class);
assertEquals(docName, docResp.getName());
assertNotNull(docResp.getContent());
- assertEquals("ISO-8859-1", docResp.getContent().getEncoding());
assertTrue(docResp.getContent().getSizeInBytes().intValue() > 0);
- assertEquals(MimetypeMap.MIMETYPE_PDF, docResp.getContent().getMimeType());
+ assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, docResp.getContent().getMimeType());
+ assertEquals("ISO-8859-1", docResp.getContent().getEncoding());
PathInfo pathInfo = docResp.getPath();
assertNotNull(pathInfo);
assertTrue(pathInfo.getIsComplete());
@@ -2836,16 +2829,6 @@ public class NodeApiTest extends AbstractBaseApiTest
// check the last element is F1
assertEquals(f1.getName(), pathElements.get(pathElements.size() - 1).getName());
- // update the original content with different encoding
- payload = new BinaryPayload(txtFile, MimetypeMap.MIMETYPE_TEXT_PLAIN, "ISO-8859-15");
- response = putBinary(url, user1, payload, null, null, 200);
- docResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
- assertEquals(docName, docResp.getName());
- assertNotNull(docResp.getContent());
- assertEquals("ISO-8859-15", docResp.getContent().getEncoding());
- assertTrue(docResp.getContent().getSizeInBytes().intValue() > 0);
- assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, docResp.getContent().getMimeType());
-
// Download the file
response = getSingle(url, user1, null, 200);
assertNotNull(content, response.getResponse());
diff --git a/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java b/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java
index a316ca92cf..d7125d96bc 100644
--- a/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java
+++ b/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java
@@ -335,7 +335,7 @@ public class SharedLinkApiTest extends AbstractBaseApiTest
assertArrayEquals(content2Text.getBytes(), response.getResponseAsBytes());
responseHeaders = response.getHeaders();
assertNotNull(responseHeaders);
- assertEquals(file2_MimeType+";charset=UTF-8", responseHeaders.get("Content-Type"));
+ assertEquals(file2_MimeType+";charset=ISO-8859-1", responseHeaders.get("Content-Type"));
assertNotNull(responseHeaders.get("Expires"));
assertNotNull(responseHeaders.get(LAST_MODIFIED_HEADER));
assertEquals("attachment; filename=\"" + fileName2 + "\"; filename*=UTF-8''" + fileName2 + "", responseHeaders.get("Content-Disposition"));
diff --git a/source/test-java/org/alfresco/rest/api/tests/util/MultiPartBuilder.java b/source/test-java/org/alfresco/rest/api/tests/util/MultiPartBuilder.java
index 9aae42d633..98db4aaaae 100644
--- a/source/test-java/org/alfresco/rest/api/tests/util/MultiPartBuilder.java
+++ b/source/test-java/org/alfresco/rest/api/tests/util/MultiPartBuilder.java
@@ -190,6 +190,11 @@ public class MultiPartBuilder
private final String mimetype;
private final String encoding;
+ public FileData(String fileName, File file)
+ {
+ this(fileName, file, null, null);
+ }
+
public FileData(String fileName, File file, String mimetype)
{
this(fileName, file, mimetype, null);