/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.servlets.post.impl.operations; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.jcr.RepositoryException; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import org.apache.commons.io.IOUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.commons.testing.sling.MockResourceResolver; import org.apache.sling.servlets.post.AbstractPostResponse; import org.apache.sling.servlets.post.Modification; import org.apache.sling.servlets.post.PostResponse; import org.apache.sling.servlets.post.impl.helper.MockSlingHttpServlet3Request; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class StreamingUploadOperationTest { private static final Logger LOG = LoggerFactory.getLogger(StreamingUploadOperationTest.class); private StreamedUploadOperation streamedUplodOperation; @Before public void before() { streamedUplodOperation = new StreamedUploadOperation(); } @After public void after() { } @Test public void test() throws PersistenceException, RepositoryException, UnsupportedEncodingException { List<Modification> changes = new ArrayList<>(); PostResponse response = new AbstractPostResponse() { @Override protected void doSend(HttpServletResponse response) throws IOException { } @Override public void onChange(String type, String... arguments) { } @Override public String getPath() { return "/test/upload/location"; } }; List<Part> partsList = new ArrayList<>(); partsList.add(new MockPart("formfield1", null, null, 0, new ByteArrayInputStream("testformfield1".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("formfield2", null, null, 0, new ByteArrayInputStream("testformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("test1.txt", "text/plain", "test1bad.txt", 4, new ByteArrayInputStream("test".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("*", "text/plain2", "test2.txt", 8, new ByteArrayInputStream("test1234".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("badformfield2", null, null, 0, new ByteArrayInputStream("testbadformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP)); final Iterator<Part> partsIterator = partsList.iterator(); final Map<String, Resource> repository = new HashMap<>(); final ResourceResolver resourceResolver = new MockResourceResolver() { @Override public Resource getResource(String path) { Resource resource = repository.get(path); if ( resource == null ) { if ( "/test/upload/location".equals(path)) { resource = new MockRealResource(this, path, "sling:Folder"); repository.put(path,resource); LOG.debug("Created {} ", path); } } LOG.debug("Resource {} is {} {}", path, resource, ResourceUtil.isSyntheticResource(resource)); return resource; } @Override public Iterable<Resource> getChildren(Resource resource) { return null; } @Override public void delete(Resource resource) throws PersistenceException { } @Override public Resource create(Resource resource, String s, Map<String, Object> map) throws PersistenceException { Resource childResource = resource.getChild(s); if ( childResource != null) { throw new IllegalArgumentException("Child "+s+" already exists "); } Resource newResource = new MockRealResource(this, resource.getPath()+"/"+s, (String)map.get("sling:resourceType"), map); repository.put(newResource.getPath(), newResource); return newResource; } @Override public void revert() { } @Override public void commit() throws PersistenceException { LOG.debug("Committing"); for(Map.Entry<String, Resource> e : repository.entrySet()) { LOG.debug("Committing {} ", e.getKey()); Resource r = e.getValue(); ModifiableValueMap vm = r.adaptTo(ModifiableValueMap.class); for (Map.Entry<String, Object> me : vm.entrySet()) { if (me.getValue() instanceof InputStream) { try { String value = IOUtils.toString((InputStream) me.getValue()); LOG.debug("Converted {} {} ", me.getKey(), value); vm.put(me.getKey(), value); } catch (IOException e1) { throw new PersistenceException("Failed to commit input stream", e1); } } } LOG.debug("Converted {} ", vm); } LOG.debug("Committted {} ", repository); } @Override public boolean hasChanges() { return false; } }; SlingHttpServletRequest request = new MockSlingHttpServlet3Request(null, null, null, null, null) { @Override public Object getAttribute(String name) { if ( "request-parts-iterator".equals(name)) { return partsIterator; } return super.getAttribute(name); } @Override public ResourceResolver getResourceResolver() { return resourceResolver; } }; streamedUplodOperation.doRun(request, response, changes); { Resource r = repository.get("/test/upload/location/test1.txt"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:file", m.get("jcr:primaryType")); } { Resource r = repository.get("/test/upload/location/test1.txt/jcr:content"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:resource", m.get("jcr:primaryType")); Assert.assertTrue(m.get("jcr:lastModified") instanceof Calendar); Assert.assertEquals("text/plain", m.get("jcr:mimeType")); Assert.assertEquals("test", m.get("jcr:data")); } { Resource r = repository.get("/test/upload/location/test2.txt"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:file", m.get("jcr:primaryType")); } { Resource r = repository.get("/test/upload/location/test2.txt/jcr:content"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:resource", m.get("jcr:primaryType")); Assert.assertTrue(m.get("jcr:lastModified") instanceof Calendar); Assert.assertEquals("text/plain2", m.get("jcr:mimeType")); Assert.assertEquals("test1234", m.get("jcr:data")); } } @Test public void testParts() throws PersistenceException, RepositoryException, UnsupportedEncodingException { List<Modification> changes = new ArrayList<>(); PostResponse response = new AbstractPostResponse() { @Override protected void doSend(HttpServletResponse response) throws IOException { } @Override public void onChange(String type, String... arguments) { } @Override public String getPath() { return "/test/upload/location"; } }; List<Part> partsList = new ArrayList<>(); partsList.add(new MockPart("test1.txt@Length", null, null, 0, new ByteArrayInputStream("8".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("test1.txt@Offset", null, null, 0, new ByteArrayInputStream("0".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart( "test1.txt", "text/plain", "test1bad.txt", 4, new ByteArrayInputStream("test".getBytes("UTF-8")), mapOf("Content-Length", "4"))); partsList.add(new MockPart("test1.txt@Offset", null, null, 0, new ByteArrayInputStream("4".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart( "test1.txt", "text/plain", "test1bad.txt", 4, new ByteArrayInputStream("part".getBytes("UTF-8")), mapOf("Content-Length", "4"))); partsList.add(new MockPart("*", "text/plain2", "test2.txt", 8, new ByteArrayInputStream("test1234".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("badformfield2", null, null, 0, new ByteArrayInputStream("testbadformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP)); final Iterator<Part> partsIterator = partsList.iterator(); final Map<String, Resource> repository = new HashMap<>(); final ResourceResolver resourceResolver = new MockResourceResolver() { @Override public Resource getResource(String path) { Resource resource = repository.get(path); if ( resource == null ) { if ( "/test/upload/location".equals(path)) { resource = new MockRealResource(this, path, "sling:Folder"); repository.put(path,resource); LOG.debug("Created {} ", path); } } LOG.debug("Resource {} is {} {}", path, resource, ResourceUtil.isSyntheticResource(resource)); return resource; } @Override public Iterable<Resource> getChildren(Resource resource) { List<Resource> children = new ArrayList<>(); for(Map.Entry<String, Resource> e : repository.entrySet()) { if (isChild(resource.getPath(), e.getKey())) { children.add(e.getValue()); } } return children; } private boolean isChild(String path, String key) { if ( key.length() > path.length() && key.startsWith(path)) { return !key.substring(path.length()+1).contains("/"); } return false; } @Override public Iterator<Resource> listChildren(Resource parent) { return getChildren(parent).iterator(); } @Override public void delete(Resource resource) throws PersistenceException { } @Override public Resource create(Resource resource, String s, Map<String, Object> map) throws PersistenceException { Resource childResource = resource.getChild(s); if ( childResource != null) { throw new IllegalArgumentException("Child "+s+" already exists "); } String resourceType = (String)map.get("sling:resourceType"); if ( resourceType == null) { resourceType = (String)map.get("jcr:primaryType"); } if ( resourceType == null) { LOG.warn("Resource type null for {} {} ", resource, resource.getPath()+"/"+s); } Resource newResource = new MockRealResource(this, resource.getPath()+"/"+s, resourceType, map); repository.put(newResource.getPath(), newResource); LOG.debug("Created Resource {} ", newResource.getPath()); return newResource; } @Override public void revert() { } @Override public void commit() throws PersistenceException { LOG.debug("Committing"); for(Map.Entry<String, Resource> e : repository.entrySet()) { LOG.debug("Committing {} ", e.getKey()); Resource r = e.getValue(); ModifiableValueMap vm = r.adaptTo(ModifiableValueMap.class); for (Map.Entry<String, Object> me : vm.entrySet()) { if (me.getValue() instanceof InputStream) { try { String value = IOUtils.toString((InputStream) me.getValue()); LOG.debug("Converted {} {} ", me.getKey(), value); vm.put(me.getKey(), value); } catch (IOException e1) { throw new PersistenceException("Failed to commit input stream", e1); } } } LOG.debug("Converted {} ", vm); } LOG.debug("Comittted {} ", repository); } @Override public boolean hasChanges() { return false; } }; SlingHttpServletRequest request = new MockSlingHttpServlet3Request(null, null, null, null, null) { @Override public Object getAttribute(String name) { if ( "request-parts-iterator".equals(name)) { return partsIterator; } return super.getAttribute(name); } @Override public ResourceResolver getResourceResolver() { return resourceResolver; } }; streamedUplodOperation.doRun(request, response, changes); { Resource r = repository.get("/test/upload/location/test1.txt"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:file", m.get("jcr:primaryType")); } { Resource r = repository.get("/test/upload/location/test1.txt/jcr:content"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:resource", m.get("jcr:primaryType")); Assert.assertTrue(m.get("jcr:lastModified") instanceof Calendar); Assert.assertEquals("text/plain", m.get("jcr:mimeType")); Assert.assertEquals("testpart", m.get("jcr:data")); } { Resource r = repository.get("/test/upload/location/test2.txt"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:file", m.get("jcr:primaryType")); } { Resource r = repository.get("/test/upload/location/test2.txt/jcr:content"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:resource", m.get("jcr:primaryType")); Assert.assertTrue(m.get("jcr:lastModified") instanceof Calendar); Assert.assertEquals("text/plain2", m.get("jcr:mimeType")); Assert.assertEquals("test1234", m.get("jcr:data")); } } @Test public void testPartsContentRange() throws PersistenceException, RepositoryException, UnsupportedEncodingException { List<Modification> changes = new ArrayList<>(); PostResponse response = new AbstractPostResponse() { @Override protected void doSend(HttpServletResponse response) throws IOException { } @Override public void onChange(String type, String... arguments) { } @Override public String getPath() { return "/test/upload/location"; } }; List<Part> partsList = new ArrayList<>(); partsList.add(new MockPart("formfield1", null, null, 0, new ByteArrayInputStream("testformfield1".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("formfield2", null, null, 0, new ByteArrayInputStream("testformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart( "test1.txt", "text/plain", "test1bad.txt", 4, new ByteArrayInputStream("test".getBytes("UTF-8")), mapOf("Content-Range","bytes 0-3/8", "Content-Length", "4"))); partsList.add(new MockPart( "test1.txt", "text/plain", "test1bad.txt", 4, new ByteArrayInputStream("part".getBytes("UTF-8")), mapOf("Content-Range","bytes 4-7/8", "Content-Length", "4"))); partsList.add(new MockPart("*", "text/plain2", "test2.txt", 8, new ByteArrayInputStream("test1234".getBytes("UTF-8")), Collections.EMPTY_MAP)); partsList.add(new MockPart("badformfield2", null, null, 0, new ByteArrayInputStream("testbadformfield2".getBytes("UTF-8")), Collections.EMPTY_MAP)); final Iterator<Part> partsIterator = partsList.iterator(); final Map<String, Resource> repository = new HashMap<>(); final ResourceResolver resourceResolver = new MockResourceResolver() { @Override public Resource getResource(String path) { Resource resource = repository.get(path); if ( resource == null ) { if ( "/test/upload/location".equals(path)) { resource = new MockRealResource(this, path, "sling:Folder"); repository.put(path,resource); LOG.debug("Created {} ", path); } } LOG.debug("Resource {} is {} {}", path, resource, ResourceUtil.isSyntheticResource(resource)); return resource; } @Override public Iterable<Resource> getChildren(Resource resource) { List<Resource> children = new ArrayList<>(); for(Map.Entry<String, Resource> e : repository.entrySet()) { if (isChild(resource.getPath(), e.getKey())) { children.add(e.getValue()); } } return children; } private boolean isChild(String path, String key) { if ( key.length() > path.length() && key.startsWith(path)) { return !key.substring(path.length()+1).contains("/"); } return false; } @Override public Iterator<Resource> listChildren(Resource parent) { return getChildren(parent).iterator(); } @Override public void delete(Resource resource) throws PersistenceException { } @Override public Resource create(Resource resource, String s, Map<String, Object> map) throws PersistenceException { Resource childResource = resource.getChild(s); if ( childResource != null) { throw new IllegalArgumentException("Child "+s+" already exists "); } String resourceType = (String)map.get("sling:resourceType"); if ( resourceType == null) { resourceType = (String)map.get("jcr:primaryType"); } if ( resourceType == null) { LOG.warn("Resource type null for {} {} ", resource, resource.getPath()+"/"+s); } Resource newResource = new MockRealResource(this, resource.getPath()+"/"+s, resourceType, map); repository.put(newResource.getPath(), newResource); LOG.debug("Created Resource {} ", newResource.getPath()); return newResource; } @Override public void revert() { } @Override public void commit() throws PersistenceException { LOG.debug("Committing"); for(Map.Entry<String, Resource> e : repository.entrySet()) { LOG.debug("Committing {} ", e.getKey()); Resource r = e.getValue(); ModifiableValueMap vm = r.adaptTo(ModifiableValueMap.class); for (Map.Entry<String, Object> me : vm.entrySet()) { if (me.getValue() instanceof InputStream) { try { String value = IOUtils.toString((InputStream) me.getValue()); LOG.debug("Converted {} {} ", me.getKey(), value); vm.put(me.getKey(), value); } catch (IOException e1) { throw new PersistenceException("Failed to commit input stream", e1); } } } LOG.debug("Converted {} ", vm); } LOG.debug("Comittted {} ", repository); } @Override public boolean hasChanges() { return false; } }; SlingHttpServletRequest request = new MockSlingHttpServlet3Request(null, null, null, null, null) { @Override public Object getAttribute(String name) { if ( "request-parts-iterator".equals(name)) { return partsIterator; } return super.getAttribute(name); } @Override public ResourceResolver getResourceResolver() { return resourceResolver; } }; streamedUplodOperation.doRun(request, response, changes); { Resource r = repository.get("/test/upload/location/test1.txt"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:file", m.get("jcr:primaryType")); } { Resource r = repository.get("/test/upload/location/test1.txt/jcr:content"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:resource", m.get("jcr:primaryType")); Assert.assertTrue(m.get("jcr:lastModified") instanceof Calendar); Assert.assertEquals("text/plain", m.get("jcr:mimeType")); Assert.assertEquals("testpart", m.get("jcr:data")); } { Resource r = repository.get("/test/upload/location/test2.txt"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:file", m.get("jcr:primaryType")); } { Resource r = repository.get("/test/upload/location/test2.txt/jcr:content"); Assert.assertNotNull(r); ValueMap m = r.adaptTo(ValueMap.class); Assert.assertNotNull(m); Assert.assertEquals("nt:resource", m.get("jcr:primaryType")); Assert.assertTrue(m.get("jcr:lastModified") instanceof Calendar); Assert.assertEquals("text/plain2", m.get("jcr:mimeType")); Assert.assertEquals("test1234", m.get("jcr:data")); } } private Map<String,Object> mapOf(String ... s) { Map<String, Object> m = new HashMap<>(); for (int i = 0; i < s.length; i+=2) { m.put(s[i],s[i+1]); } return m; } }