incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bdelacre...@apache.org
Subject svn commit: r1553094 [2/5] - in /sling/trunk/samples/mail-archive: ./ docs/ server/ server/src/ server/src/main/ server/src/main/java/ server/src/main/java/org/ server/src/main/java/org/apache/ server/src/main/java/org/apache/sling/ server/src/main/jav...
Date Mon, 23 Dec 2013 11:11:17 GMT
Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,280 @@
+/*
+ * 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.discovery.impl.setup;
+
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+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 org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+
+// TODO this should come from a commons module, for now
+// it's duplicated from the discovery module
+public class MockedResource extends SyntheticResource {
+
+	private final MockedResourceResolver mockedResourceResolver;
+	private Session session;
+
+	public MockedResource(MockedResourceResolver resourceResolver, String path,
+			String resourceType) {
+		super(resourceResolver, path, resourceType);
+		mockedResourceResolver = resourceResolver;
+
+		resourceResolver.register(this);
+	}
+
+	private Session getSession() {
+		synchronized (this) {
+			if (session == null) {
+				try {
+					session = mockedResourceResolver.getSession();
+				} catch (RepositoryException e) {
+					throw new RuntimeException("RepositoryException: " + e, e);
+				}
+			}
+			return session;
+		}
+	}
+
+	@Override
+	protected void finalize() throws Throwable {
+		close();
+		super.finalize();
+	}
+
+	public void close() {
+		synchronized (this) {
+			if (session != null) {
+				if (session.isLive()) {
+					session.logout();
+				}
+				session = null;
+			}
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+		if (type.equals(Node.class)) {
+			try {
+				return (AdapterType) getSession().getNode(getPath());
+			} catch (Exception e) {
+				throw new RuntimeException("Exception occurred: " + e, e);
+			}
+		} else if (type.equals(ValueMap.class)) {
+			try {
+				Session session = getSession();
+				Node node = session.getNode(getPath());
+				HashMap<String, Object> map = new HashMap<String, Object>();
+
+				PropertyIterator properties = node.getProperties();
+				while (properties.hasNext()) {
+					Property p = properties.nextProperty();
+					if (p.getType() == PropertyType.BOOLEAN) {
+						map.put(p.getName(), p.getBoolean());
+					} else if (p.getType() == PropertyType.STRING) {
+						map.put(p.getName(), p.getString());
+					} else if (p.getType() == PropertyType.DATE) {
+						map.put(p.getName(), p.getDate().getTime());
+					} else if (p.getType() == PropertyType.NAME) {
+						map.put(p.getName(), p.getName());
+					} else {
+						throw new RuntimeException(
+								"Unsupported property type: " + p.getType());
+					}
+				}
+				ValueMap valueMap = new ValueMapDecorator(map);
+				return (AdapterType) valueMap;
+			} catch (Exception e) {
+				e.printStackTrace();
+				return null;
+			}
+		} else if (type.equals(ModifiableValueMap.class)) {
+			return (AdapterType) new ModifiableValueMap() {
+
+				public Collection<Object> values() {
+					throw new UnsupportedOperationException();
+				}
+
+				public int size() {
+					throw new UnsupportedOperationException();
+				}
+
+				public Object remove(Object arg0) {
+					Session session = getSession();
+					try{
+						final Node node = session.getNode(getPath());
+						final Property p = node.getProperty(String.valueOf(arg0));
+						if (p!=null) {
+							p.remove();
+						}
+						// this is not according to the spec - but OK for tests since
+						// the return value is never used
+								return null;
+					} catch(PathNotFoundException pnfe) {
+						// perfectly fine
+						return null;
+					} catch(RepositoryException e) {
+						throw new RuntimeException(e);
+					}
+				}
+
+				public void putAll(Map<? extends String, ? extends Object> arg0) {
+					throw new UnsupportedOperationException();
+				}
+
+				public Object put(String arg0, Object arg1) {
+					Session session = getSession();
+					try{
+						final Node node = session.getNode(getPath());
+						Object result = null;
+						if (node.hasProperty(arg0)) {
+							final Property previous = node.getProperty(arg0);
+							if (previous==null) {
+								// null
+							} else if (previous.getType() == PropertyType.STRING) {
+								result = previous.getString();
+							} else if (previous.getType() == PropertyType.DATE) {
+								result = previous.getDate();
+							} else if (previous.getType() == PropertyType.BOOLEAN) {
+								result = previous.getBoolean();
+							} else {
+								throw new UnsupportedOperationException();
+							}
+						}
+						if (arg1 instanceof String) {
+							node.setProperty(arg0, (String)arg1);
+						} else if (arg1 instanceof Long) {
+							node.setProperty(arg0, (Long)arg1);
+						} else if (arg1 instanceof Calendar) {
+							node.setProperty(arg0, (Calendar)arg1);
+						} else if (arg1 instanceof Boolean) {
+							node.setProperty(arg0, (Boolean)arg1);
+						} else {
+							throw new UnsupportedOperationException();
+						}
+						return result;
+					} catch(RepositoryException e) {
+						throw new RuntimeException(e);
+					}
+				}
+
+				public Set<String> keySet() {
+					Session session = getSession();
+					try {
+						final Node node = session.getNode(getPath());
+						final PropertyIterator pi = node.getProperties();
+						final Set<String> result = new HashSet<String>();
+						while(pi.hasNext()) {
+							final Property p = pi.nextProperty();
+							result.add(p.getName());
+						}
+						return result;
+					} catch (RepositoryException e) {
+						throw new RuntimeException(e);
+					}
+				}
+
+				public boolean isEmpty() {
+					throw new UnsupportedOperationException();
+				}
+
+				public Object get(Object arg0) {
+					try {
+						Node node = session.getNode(getPath());
+						return node.getProperty(String.valueOf(arg0));
+					} catch (PathNotFoundException e) {
+						return null;
+					} catch (RepositoryException e) {
+						e.printStackTrace();
+						throw new RuntimeException();
+					}
+					// throw new UnsupportedOperationException();
+				}
+
+				public Set<Entry<String, Object>> entrySet() {
+					throw new UnsupportedOperationException();
+				}
+
+				public boolean containsValue(Object arg0) {
+					throw new UnsupportedOperationException();
+				}
+
+				public boolean containsKey(Object arg0) {
+					Session session = getSession();
+					try{
+						final Node node = session.getNode(getPath());
+						return node.hasProperty(String.valueOf(arg0));
+					} catch(RepositoryException re) {
+						throw new RuntimeException(re);
+					}
+				}
+
+				public void clear() {
+					throw new UnsupportedOperationException();
+				}
+
+				public <T> T get(String name, T defaultValue) {
+					throw new UnsupportedOperationException();
+				}
+
+				public <T> T get(String name, Class<T> type) {
+					Session session = getSession();
+					try{
+						final Node node = session.getNode(getPath());
+						if (node==null) {
+							return null;
+						}
+						Property p = node.getProperty(name);
+						if (p==null) {
+							return null;
+						}
+						if (type.equals(Calendar.class)) {
+							return (T) p.getDate();
+						} else if (type.equals(String.class)) {
+							return (T) p.getString();
+						} else {
+							throw new UnsupportedOperationException();
+						}
+					} catch(RepositoryException e) {
+						throw new RuntimeException(e);
+					}
+				}
+			};
+		} else {
+			return super.adaptTo(type);
+		}
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,321 @@
+/*
+ * 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.discovery.impl.setup;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.api.resource.LoginException;
+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.commons.testing.jcr.RepositoryProvider;
+
+//TODO this should come from a commons module, for now
+//it's duplicated from the discovery module
+public class MockedResourceResolver implements ResourceResolver {
+
+	private final Repository repository;
+	
+	private Session session;
+	
+    private List<MockedResource> resources = new LinkedList<MockedResource>();
+
+    public MockedResourceResolver() throws RepositoryException {
+    	this(null);
+    }
+
+    public MockedResourceResolver(Repository repositoryOrNull) throws RepositoryException {
+    	if (repositoryOrNull==null) {
+    		this.repository = RepositoryProvider.instance().getRepository();
+    	} else {
+    		this.repository = repositoryOrNull;
+    	}
+    }
+    
+    public Session getSession() throws RepositoryException {
+        synchronized (this) {
+            if (session != null) {
+                return session;
+            }
+            session = createSession();
+            return session;
+        }
+    }
+
+    private Repository getRepository() {
+    	return repository;
+    }
+    
+    private Session createSession() throws RepositoryException {
+        final Credentials credentials = new SimpleCredentials("admin",
+                "admin".toCharArray());
+        return repository.login(credentials, "default");
+    }
+	
+	
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        if (type.equals(Session.class)) {
+            try {
+                return (AdapterType) getSession();
+            } catch (RepositoryException e) {
+                throw new RuntimeException("RepositoryException: " + e, e);
+            }
+        } else if (type.equals(Repository.class)) {
+        	return (AdapterType) getRepository();
+        }
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Resource resolve(HttpServletRequest request, String absPath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Resource resolve(String absPath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Deprecated
+    public Resource resolve(HttpServletRequest request) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public String map(String resourcePath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public String map(HttpServletRequest request, String resourcePath) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Resource getResource(String path) {
+        Session session;
+        try {
+            session = getSession();
+            session.getNode(path);
+        } catch (PathNotFoundException e) {
+            return null;
+        } catch (RepositoryException e) {
+            throw new RuntimeException("RepositoryException: " + e, e);
+        }
+        return new MockedResource(this, path, "nt:unstructured");
+    }
+
+    public Resource getResource(Resource base, String path) {
+        if (base.getPath().equals("/")) {
+            return getResource("/" + path);
+        } else {
+            return getResource(base.getPath() + "/" + path);
+        }
+    }
+
+    public String[] getSearchPath() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<Resource> listChildren(Resource parent) {
+        try {
+            Node node = parent.adaptTo(Node.class);
+            final NodeIterator nodes = node.getNodes();
+            return new Iterator<Resource>() {
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+
+                public Resource next() {
+                    Node next = nodes.nextNode();
+                    try {
+                        return new MockedResource(MockedResourceResolver.this,
+                                next.getPath(), "nt:unstructured");
+                    } catch (RepositoryException e) {
+                        throw new RuntimeException("RepositoryException: " + e,
+                                e);
+                    }
+                }
+
+                public boolean hasNext() {
+                    return nodes.hasNext();
+                }
+            };
+        } catch (RepositoryException e) {
+            throw new RuntimeException("RepositoryException: " + e, e);
+        }
+    }
+
+    public Iterable<Resource> getChildren(Resource parent) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<Resource> findResources(String query, String language) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<Map<String, Object>> queryResources(String query,
+            String language) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public ResourceResolver clone(Map<String, Object> authenticationInfo)
+            throws LoginException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public boolean isLive() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public void close() {
+        Iterator<MockedResource> it = resources.iterator();
+        while (it.hasNext()) {
+            MockedResource r = it.next();
+            r.close();
+        }
+        if (session != null) {
+            if (session.isLive()) {
+                session.logout();
+            }
+            session = null;
+        }
+    }
+
+    public void register(MockedResource mockedResource) {
+        resources.add(mockedResource);
+    }
+
+    public String getUserID() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Iterator<String> getAttributeNames() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public Object getAttribute(String name) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public void delete(Resource resource) throws PersistenceException {
+        if (resources.contains(resource)) {
+            resources.remove(resource);
+            Node node = resource.adaptTo(Node.class);
+            try {
+                node.remove();
+            } catch (RepositoryException e) {
+                throw new PersistenceException("RepositoryException: "+e, e);
+            }
+        } else {
+            throw new UnsupportedOperationException("Not implemented");
+        }
+    }
+
+    public Resource create(Resource parent, String name,
+            Map<String, Object> properties) throws PersistenceException {
+        final Node parentNode = parent.adaptTo(Node.class);
+        try {
+            final Node child;
+            if (properties!=null && properties.containsKey("jcr:primaryType")) {
+                child = parentNode.addNode(name, (String) properties.get("jcr:primaryType"));
+            } else {
+                child = parentNode.addNode(name);
+            }
+            if (properties!=null) {
+                final Iterator<Entry<String, Object>> it = properties.entrySet().iterator();
+                while(it.hasNext()) {
+                    final Entry<String, Object> entry = it.next();
+                    if (entry.getKey().equals("jcr:primaryType")) {
+                        continue;
+                    }
+                    if (entry.getValue() instanceof String) {
+                        child.setProperty(entry.getKey(), (String)entry.getValue());
+                    } else if (entry.getValue() instanceof Boolean) {
+                        child.setProperty(entry.getKey(), (Boolean)entry.getValue());
+                    } else if (entry.getValue() instanceof InputStream) {
+                        child.setProperty(entry.getKey(), new String(IOUtils.toByteArray((InputStream)entry.getValue())));
+                    } else {
+                        throw new UnsupportedOperationException("Not implemented");
+                    }
+                }
+            }
+            return getResource(parent, name);
+        } catch (RepositoryException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void revert() {
+        try {
+            this.session.refresh(false);
+        } catch (final RepositoryException re) {
+            throw new RuntimeException("Unable to commit changes.", re);
+        }
+    }
+
+    public void commit() throws PersistenceException {
+        try {
+            this.session.save();
+        } catch (final RepositoryException re) {
+            throw new PersistenceException("Unable to commit changes.", re);
+        }
+    }
+
+    public boolean hasChanges() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    public String getParentResourceType(Resource resource) {
+        //  Auto-generated method stub
+        return null;
+    }
+
+    public String getParentResourceType(String resourceType) {
+        //  Auto-generated method stub
+        return null;
+    }
+
+    public boolean isResourceType(Resource resource, String resourceType) {
+        //  Auto-generated method stub
+        return false;
+    }
+
+    public void refresh() {
+        //  Auto-generated method stub
+
+    }
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplAttachmentsTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplAttachmentsTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplAttachmentsTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplAttachmentsTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,205 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.getResourcePath;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.CONTENT;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.LIST_ID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Random;
+
+import org.apache.james.mime4j.dom.BinaryBody;
+import org.apache.james.mime4j.dom.Body;
+import org.apache.james.mime4j.dom.Entity;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
+import org.apache.james.mime4j.dom.TextBody;
+import org.apache.james.mime4j.message.BodyPart;
+import org.apache.james.mime4j.message.MessageImpl;
+import org.apache.james.mime4j.message.MultipartImpl;
+import org.apache.james.mime4j.storage.StorageBodyFactory;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.stream.RawField;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.discovery.impl.setup.MockedResourceResolver;
+import org.apache.sling.mailarchiveserver.util.MailArchiveServerConstants;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MessageStoreImplAttachmentsTest {
+    private ResourceResolver resolver;
+    private Resource testRoot;
+    private MessageStoreImpl store;
+
+    /**
+     * Some code is taken from http://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/src/main/java/org/apache/sling/launchpad/testservices/serversidetests/WriteableResourcesTest.java
+     */
+    @Before
+    public void setup() throws Exception {
+        resolver = new MockedResourceResolver();
+        assertNotNull("Expecting non-null ResourceResolver", resolver);
+        final Resource root = resolver.getResource("/");
+        assertNotNull("Expecting non-null root Resource", root);
+        final String path = getClass().getSimpleName() + "_" + System.currentTimeMillis();
+        testRoot = resolver.create(root, path, null);
+        resolver.commit();
+
+        store = new MessageStoreImpl() {
+            protected ResourceResolver getResourceResolver() {
+                return resolver;
+            }
+        };
+        store.threadKeyGen = new ThreadKeyGeneratorImpl();
+        store.attachmentFilter = new AttachmentFilterImpl();
+        store.archivePath = testRoot.getPath() + "/";
+        store.resourceTypeKey = MessageStoreImplRepositoryTest.TEST_RT_KEY;
+    }
+
+    @After
+    public void cleanup() throws Exception {
+        resolver.close();
+        resolver = null;
+        testRoot = null;
+        store = null;
+    }
+
+    @Test
+    public void simpleMultipartMessageTest() throws IOException {
+        Multipart multipart = new MultipartImpl("mixed");
+        BodyPart att0 = createTextBody("This is the first part of the template..", "plain", true);
+        multipart.addBodyPart(att0);
+        BodyPart att1 = createRandomBinaryAttachment(200);
+        multipart.addBodyPart(att1);
+        BodyPart att2 = createRandomBinaryAttachment(300);
+        multipart.addBodyPart(att2);
+        BodyPart att3 = createTextBody("Some sample text here...?!", "html", true);
+        multipart.addBodyPart(att3);
+        BodyPart att4 = createRandomBinaryAttachment(100);
+        multipart.addBodyPart(att4);
+        BodyPart att5 = createTextBody("Some other text here...?!", "plain", true);
+        multipart.addBodyPart(att5);
+        
+        MessageImpl message = new MessageImpl();
+        message.setMultipart(multipart);
+        message.setSubject("Template message");
+        message.setDate(new Date());
+        message.getHeader().setField(new RawField(LIST_ID, "<list.example.com>"));
+
+        assertSaveMessageWithAttachments(message, 6);
+    }
+    
+    @Test
+    public void recursiveMultipartMessageTest() throws IOException {
+        Multipart multipart = new MultipartImpl("mixed");
+        BodyPart att1 = createRandomBinaryAttachment(100);
+        multipart.addBodyPart(att1);
+        BodyPart att2 = createRandomBinaryAttachment(133);
+        multipart.addBodyPart(att2);
+        
+        Multipart nestedMultipart = new MultipartImpl("mixed");
+        BodyPart nBody = createTextBody("Some sample text here...?!", "plain", false);
+        nestedMultipart.addBodyPart(nBody);
+        BodyPart nAtt1 = createRandomBinaryAttachment(300);
+        nestedMultipart.addBodyPart(nAtt1);
+        BodyPart NAtt2 = createRandomBinaryAttachment(100);
+        nestedMultipart.addBodyPart(NAtt2);
+        BodyPart nAtt3 = createTextBody("Some other text here...<br>?!", "html", true);
+        nestedMultipart.addBodyPart(nAtt3);
+        
+        BodyPart nestedMessage = new BodyPart();
+        nestedMessage.setMultipart(nestedMultipart);
+        multipart.addBodyPart(nestedMessage);
+
+        MessageImpl message = new MessageImpl();
+        message.setMultipart(multipart);
+        message.setSubject("Template message");
+        message.setDate(new Date());
+        message.getHeader().setField(new RawField(LIST_ID, "<list.example.com>"));
+
+        assertSaveMessageWithAttachments(message, 5);
+    }
+
+    private void assertSaveMessageWithAttachments(Message msg, int num) throws IOException {
+        store.save(msg);
+
+        List<BodyPart> attList = new LinkedList<BodyPart>();
+        MessageStoreImpl.recursiveMultipartProcessing((Multipart) msg.getBody(), new StringBuilder(), new StringBuilder(), false, attList); 
+        @SuppressWarnings("unchecked")
+        Queue<BodyPart> attachmentsMsg = (Queue<BodyPart>) attList;
+        assertTrue("No attachments found", attachmentsMsg.size() > 0);
+        assertEquals("", num, attachmentsMsg.size());
+        
+        final Resource r = resolver.getResource(getResourcePath(msg, store));
+        assertNotNull("Expecting non-null Resource", r);
+        for (Resource aRes : r.getChildren()) {
+            final ModifiableValueMap aMap = aRes.adaptTo(ModifiableValueMap.class);
+            BodyPart aMsg = attachmentsMsg.poll();
+            assertNotNull("JCR contains more attachments", aMsg);
+
+            for (Field f : aMsg.getHeader().getFields()) {
+                String name = f.getName();
+                assertEquals("Field "+name+" is different", (aMap.get(name, String.class)), f.getBody());
+            }
+            
+            if (aMsg.getBody() instanceof TextBody) {
+                assertEquals("Content is not the same", MessageStoreImpl.getTextPart(aMsg), aMap.get(CONTENT, String.class));
+            } else if (aMsg.getBody() instanceof BinaryBody) {
+                assertEquals("Content is not the same", getBinPart(aMsg), aMap.get(CONTENT, String.class));
+            } else {
+                fail("Unknown type of attachment body");
+            }
+        }
+        assertEquals("Message contains more attachments", attachmentsMsg.poll(), null);
+    }
+
+    private String getBinPart(Entity part) throws IOException {
+        BinaryBody bb = (BinaryBody) part.getBody();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        bb.writeTo(baos);
+        return new String(baos.toByteArray());
+    }
+
+    /*
+     * taken from http://svn.apache.org/repos/asf/james/mime4j/trunk/examples/src/main/java/org/apache/james/mime4j/samples/transform/TransformMessage.java
+     */
+    private static BodyPart createTextBody(String text, String subtype, boolean isAttachment) {
+        TextBody body = new StorageBodyFactory().textBody(text, MailArchiveServerConstants.DEFAULT_ENCODER.charset().name());
+
+        BodyPart bodyPart = new BodyPart();
+        if (isAttachment) {
+            bodyPart.setContentDisposition("attachment", "file"+Math.random());
+        }
+        bodyPart.setText(body, subtype);
+
+        return bodyPart;
+    }
+
+    /*
+     * taken from http://svn.apache.org/repos/asf/james/mime4j/trunk/examples/src/main/java/org/apache/james/mime4j/samples/transform/TransformMessage.java
+     */
+    private static BodyPart createRandomBinaryAttachment(int numberOfBytes) throws IOException {
+        byte[] data = new byte[numberOfBytes];
+        new Random().nextBytes(data);
+
+        Body body = new StorageBodyFactory().binaryBody(new ByteArrayInputStream(data));
+
+        BodyPart bodyPart = new BodyPart();
+        bodyPart.setContentDisposition("attachment", "file"+Math.random());
+        bodyPart.setBody(body, "application/octet-stream");
+
+        return bodyPart;
+    }
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,129 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.assertValueMap;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.getResourcePath;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.readTextFile;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.specialPathFromFilePath;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.MessageBuilder;
+import org.apache.james.mime4j.message.DefaultMessageBuilder;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.discovery.impl.setup.MockedResourceResolver;
+import org.apache.sling.mailarchiveserver.api.MboxParser;
+import org.apache.sling.mailarchiveserver.util.MailArchiveServerConstants;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MessageStoreImplRepositoryTest {
+	private ResourceResolver resolver;
+	private Resource testRoot;
+	private MessageStoreImpl store;
+
+	static final String TEST_RT_KEY = "sling_resourceType";
+	private static final String TEST_FOLDER = "test_files/";
+	private static final String BODY_SUFFIX = "_body";
+	private static final String HEADERS_SUFFIX = "_headers";
+
+	private static final String SINGLEPART_FILE = "singlepart.txt";
+	private static final String MULTIPART_FILE = "multipart.txt";
+	private static final String WRONGBODY_FILE = "wrongbody.txt";
+	private static final String MBOX_FILE = "three_messages.mbox";
+
+	/**
+	 * Some code is taken from http://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/src/main/java/org/apache/sling/launchpad/testservices/serversidetests/WriteableResourcesTest.java
+	 */
+	@Before
+	public void setup() throws Exception {
+		resolver = new MockedResourceResolver();
+		assertNotNull("Expecting non-null ResourceResolver", resolver);
+		final Resource root = resolver.getResource("/");
+		assertNotNull("Expecting non-null root Resource", root);
+		final String path = getClass().getSimpleName() + "_" + System.currentTimeMillis();
+		testRoot = resolver.create(root, path, null);
+		resolver.commit();
+
+		store = new MessageStoreImpl() {
+		    protected ResourceResolver getResourceResolver() {
+		        return resolver;
+		    }
+		};
+		store.threadKeyGen = new ThreadKeyGeneratorImpl();
+		store.archivePath = testRoot.getPath() + "/";
+		store.resourceTypeKey = TEST_RT_KEY;
+	}
+
+	@After
+	public void cleanup() throws Exception {
+		resolver.close();
+		resolver = null;
+		testRoot = null;
+		store = null;
+	}
+
+
+	@Test
+	public void testSaveMessage() throws FileNotFoundException, MimeException, IOException  {
+		assertSaveMessage(SINGLEPART_FILE);
+		assertSaveMessage(MULTIPART_FILE);
+		assertSaveMessage(WRONGBODY_FILE);
+	}
+
+	@Test
+	public void testStructure() throws IOException {
+		MboxParser parser = new Mime4jMboxParserImpl();
+		final File file = new File(TEST_FOLDER + MBOX_FILE);
+		store.saveAll(parser.parse(new FileInputStream(file)));
+		assertStructure();
+	}
+
+	private void assertSaveMessage(String messageFile) throws MimeException, IOException, FileNotFoundException {
+		MessageBuilder builder = new DefaultMessageBuilder();
+		Message msg = builder.parseMessage(new FileInputStream(TEST_FOLDER + messageFile));
+
+		store.save(msg);
+
+		final Resource r = resolver.getResource(getResourcePath(msg, store));
+		assertNotNull("Expecting non-null Resource", r);
+		final ModifiableValueMap m = r.adaptTo(ModifiableValueMap.class);
+
+		File bodyFile = new File(TEST_FOLDER + specialPathFromFilePath(messageFile, BODY_SUFFIX));
+		if (bodyFile.exists()) {
+			String expectedBody = readTextFile(bodyFile);
+			assertValueMap(m, "Body", expectedBody);
+		}
+
+		File headersFile = new File(TEST_FOLDER + specialPathFromFilePath(messageFile, HEADERS_SUFFIX));
+		if (headersFile.exists()) {
+			MessageStoreImplRepositoryTestUtil.assertHeaders(headersFile, m);
+		}
+
+		assertTrue(headersFile.exists() || bodyFile.exists()); // test at least something 
+	}
+
+	private void assertStructure() {
+		List<String> types = new ArrayList<String>();
+		types.add(MailArchiveServerConstants.DOMAIN_RT);
+		types.add(MailArchiveServerConstants.LIST_RT);
+		types.add(null);
+		types.add(null);
+		types.add(MailArchiveServerConstants.THREAD_RT);
+		types.add(MailArchiveServerConstants.MESSAGE_RT);
+
+		MessageStoreImplRepositoryTestUtil.assertLayer(testRoot, types, 0);
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTestUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTestUtil.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTestUtil.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTestUtil.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,142 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.getDomainNodeName;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.getListNodeName;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.makeJcrFriendly;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.james.mime4j.dom.Header;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+
+public class MessageStoreImplRepositoryTestUtil {
+
+    private static final String TEST_FILE_FIELD_SEPARATOR = " : ";
+
+    /**
+     * Code is taken from http://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/src/main/java/org/apache/sling/launchpad/testservices/serversidetests/WriteableResourcesTest.java
+     */
+    static void assertValueMap(ValueMap m, String ... keyValue) {
+        assertNotNull("Expecting non-null ValueMap", m);
+        for(int i=0 ; i< keyValue.length; i+=2) {
+            final String key = keyValue[i];
+            final String value = keyValue[i+1];
+            assertEquals("Expecting " + key + "=" + value, value, m.get(key, String.class));
+        }
+    }
+
+    static String readTextFile(File bodyFile) throws FileNotFoundException {
+        Scanner sc = null;
+        try {
+            sc = new Scanner(bodyFile);
+            String expectedBody = ""; 
+            while (sc.hasNextLine()) {
+                expectedBody += sc.nextLine() + "\n";
+            }
+            expectedBody = expectedBody.substring(0, expectedBody.length()-1);
+            return expectedBody;
+        } finally {
+            if (sc != null) {
+                sc.close();
+            }
+        }
+    }
+
+    static String specialPathFromFilePath(String fpath, String suffix, String ext) {
+        int dotIdx = fpath.lastIndexOf(".");
+        String bodyPath = fpath.substring(0, dotIdx) + suffix + "." + ext; 
+        return bodyPath;
+    }
+
+    static String specialPathFromFilePath(String fpath, String suffix) {
+        int dotIdx = fpath.lastIndexOf(".");
+        return specialPathFromFilePath(fpath, suffix, fpath.substring(dotIdx + 1));
+    }
+
+    static String getResourcePath(Message msg, MessageStoreImpl store) {
+        final Header hdr = msg.getHeader();
+        final String listIdRaw = hdr.getField("List-Id").getBody();
+        final String listId = listIdRaw.substring(1, listIdRaw.length()-1); // remove < and >
+
+        String msgId;
+        final Field msgIdField = hdr.getField("Message-ID");
+        if (msgIdField != null) {
+            msgId = msgIdField.getBody();
+            msgId = msgId.substring(1, msgId.length()-1);
+        } else {
+            msgId = Integer.toHexString(hdr.getField("Date").hashCode());
+        }
+        msgId = makeJcrFriendly(msgId);
+
+        String subject = null;
+        final Field subjectField = hdr.getField("Subject");
+        if (subjectField != null) {
+            subject = subjectField.getBody();
+        }
+
+        String threadPath = store.threadKeyGen.getThreadKey(subject);
+        String path = store.archivePath + getDomainNodeName(listId) + "/" + getListNodeName(listId) +
+                "/" + threadPath + "/" + msgId;
+        return path;
+    }
+
+    static void assertHeaders(File headersFile, ModifiableValueMap m) throws FileNotFoundException {
+        Map<String, List<String>> expectedHeaders = new HashMap<String, List<String>>();
+        Scanner sc = new Scanner(headersFile);
+        while (sc.hasNextLine()) {
+            String line = sc.nextLine();
+            if (line.startsWith("//")) 
+                continue;
+            String[] colon = line.split(TEST_FILE_FIELD_SEPARATOR);
+            String header = colon[0];
+            String value = colon[1];
+            List<String> values;
+            if ((values = expectedHeaders.get(header)) == null) {
+                values = new ArrayList<String>();
+                expectedHeaders.put(header, values);
+            }
+            values.add(value);
+        }
+        sc.close();
+
+        assertEquals("Expecting same number of headers", m.keySet().size()-2, expectedHeaders.keySet().size()); 
+        // -1 for Body (should be no htmlBody)
+        // -1 for X-original-header
+
+        for (String expectedHeader : expectedHeaders.keySet()) {
+            assertTrue("Expecting header \""+expectedHeader+"\" to exist", m.containsKey(expectedHeader));
+            String values = m.get(expectedHeader, String.class);
+            for (String expectedValue : expectedHeaders.get(expectedHeader)) {
+                assertTrue("Expecting header \""+expectedHeader+"\" to contain the value", values.contains(expectedValue));
+            }
+        }
+    }
+
+    static void assertLayer(Resource root, List<String> types, int depth) {
+        for (Resource child : root.getChildren()) {
+            final ModifiableValueMap m = child.adaptTo(ModifiableValueMap.class);
+            if (m.keySet().contains(MessageStoreImplRepositoryTest.TEST_RT_KEY)) {
+                String type = m.get(MessageStoreImplRepositoryTest.TEST_RT_KEY, String.class);
+                assertEquals(String.format("Expecting %s to have %s type", child.getPath(), types.get(depth)), types.get(depth), type);
+            }
+            if (child.getChildren().iterator().hasNext()) {
+                assertLayer(child, types, depth+1);
+            }
+        }
+
+    }
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplStaticMethodsTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplStaticMethodsTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplStaticMethodsTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplStaticMethodsTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,49 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.getDomainNodeName;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.getListNodeName;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.makeJcrFriendly;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.removeRe;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.message.MessageImpl;
+import org.apache.sling.mailarchiveserver.util.MailArchiveServerConstants;
+import org.junit.Test;
+
+public class MessageStoreImplStaticMethodsTest {
+	
+	@Test
+	public void testMakeJcrFriendly() {
+		assertEquals("Remove each char", "", makeJcrFriendly("��!@#$%^&*()+={}[]<>,/?\\;:'\""));
+		assertEquals("Substitute each char with _ char, trimming", "a", makeJcrFriendly(".a_")); 
+		assertEquals("Substitute each char with _ char", "b_e", makeJcrFriendly("b_ .-e"));
+	}
+
+	@Test
+	public void testRemoveRe() {
+		assertEquals(removeRe("abc"), "abc");
+		assertEquals(removeRe("Re:re"), "re");
+		assertEquals(removeRe("RE: abc"), "abc");
+		assertEquals(removeRe("re: RE: "), "");
+		assertEquals(removeRe(" re:  abc  "), "abc");
+		assertEquals(removeRe(" re:fw:  aw:RE: FW: subj "), "subj");
+		assertEquals(removeRe(""), "");
+		assertEquals(removeRe("     "), "");
+		assertEquals(removeRe("Re:   "), "");
+	}
+	
+	@Test
+	public void testNodeNamesFromListId() {
+		assertEquals(getListNodeName("dev.sling.apache.org"), "dev.sling");
+		assertEquals(getDomainNodeName("dev.sling.apache.org"), "apache.org");
+		assertEquals(getListNodeName("proj.apache.org"), "proj");
+		assertEquals(getDomainNodeName("proj.apache.org"), "apache.org");
+		assertEquals(getListNodeName("a.b.c.apache.org"), "a.b.c");
+		assertEquals(getDomainNodeName("a.b.c.apache.org"), "apache.org");
+	}
+	
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplCountTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplCountTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplCountTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplCountTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,61 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.james.mime4j.dom.Message;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class Mime4jMboxParserImplCountTest {
+
+	private static Mime4jMboxParserImpl parser = new Mime4jMboxParserImpl();
+	private String filePath;
+	private int expectedMessagesCount;
+	
+	static final String TEST_FOLDER = "test_files/";
+	
+	@Parameters(name="{0}")
+    public static Collection<Object[]> data() {
+        List<Object[]> params = new ArrayList<Object[]>();
+        params.add(new Object[] {TEST_FOLDER+"three_messages.mbox", 3} );
+        params.add(new Object[] {TEST_FOLDER+"mbox/jackrabbit-dev-201201.mbox", 323} );
+        params.add(new Object[] {TEST_FOLDER+"mbox/hadoop-common-dev-201202.mbox", 296} );
+        params.add(new Object[] {TEST_FOLDER+"mbox/sling-dev-201203.mbox", 227} );
+        params.add(new Object[] {TEST_FOLDER+"mbox/tomcat-dev-201204.mbox", 658} );
+        return params;
+    }
+    
+    public Mime4jMboxParserImplCountTest(String path, int count) {
+    	filePath = path;
+    	expectedMessagesCount = count;
+    }
+
+	@Test
+	public void testParse() throws IOException {
+		Iterator<Message> iter = parser.parse(new FileInputStream(new File(filePath)));
+		
+		int cnt = 0;
+		Set<Message> set = new HashSet<Message>();
+		while (iter.hasNext()) {
+			Message message = (Message) iter.next();
+			cnt++;
+			set.add(message);
+		}
+		assertEquals("Expecting correct number of messages parsed", expectedMessagesCount, cnt);
+		assertEquals("Expecting all messages unique", expectedMessagesCount, set.size());
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplStreamingTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplStreamingTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplStreamingTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplStreamingTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,78 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.sling.mailarchiveserver.impl.Mime4jMboxParserImpl.Mime4jParserIterator;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * In this class there is a test that parses big file. It will take a while to execute.
+ */
+public class Mime4jMboxParserImplStreamingTest {
+
+	private Mime4jMboxParserImpl parser = new Mime4jMboxParserImpl();
+	private final Logger log = LoggerFactory.getLogger(getClass());
+
+	private static final String TEST_FOLDER = Mime4jMboxParserImplCountTest.TEST_FOLDER;
+	private static final String TEST_FILE_NAME = TEST_FOLDER  + "mbox/tomcat-dev-201204.mbox";
+	private static final double TEST_FILE_RATIO = 1.2;
+
+	@Test
+	public void testParserIsStreaming() throws IOException {
+		File tempf = null;
+		FileInputStream fis = null;
+		FileOutputStream fos = null;
+		try {
+            final long maxMem = Runtime.getRuntime().maxMemory();
+		    log.info("Max memory={}, reading from {}, might take a while...", maxMem, TEST_FILE_NAME);
+		    log.info("TODO: this test fails with 'Size exceeds Integer.MAX_VALUE' if maxMem is over a certain limit");
+			final File fileToSample = new File(TEST_FILE_NAME);
+			final int count = (int) (maxMem * TEST_FILE_RATIO / fileToSample.length()) + 1;
+
+			fis = new FileInputStream(fileToSample);
+			final byte[] sample = new byte[(int) fileToSample.length()];
+			assertEquals("Expecting the correct number of bytes read", fis.read(sample), fileToSample.length()); 
+
+			tempf = File.createTempFile("MAS_", ".mbox");
+			fos = new FileOutputStream(tempf);
+			for (int i = 0; i < count; i++) {
+				fos.write(sample);
+			}
+            fos.flush();
+            fos.close();
+			fos = null;
+
+			parser.parse(new FileInputStream(tempf));
+
+		} catch(OutOfMemoryError e) {
+			fail("Got OutOfMemoryError, looks like the Parser is not streaming");
+		} finally {
+			if (tempf != null) {
+				tempf.delete();
+			}
+			if (fis != null) {
+				fis.close();
+			}
+			if (fos != null) {
+				fos.close();
+			}
+		}
+	}
+
+	@Test
+	public void testTempFileIsDeleted() throws IOException {
+		File testFile = new File(TEST_FOLDER + "mbox/tomcat-dev-201204.mbox");
+		Mime4jParserIterator iter = (Mime4jParserIterator) parser.parse(new FileInputStream(testFile));
+		assertFalse("Expecting temp file to be deleted", new File(iter.tempFileAbsPath).exists());
+	}	
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,73 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.readTextFile;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.*;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImplRepositoryTestUtil.specialPathFromFilePath;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.james.mime4j.dom.Entity;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
+import org.apache.james.mime4j.message.BodyPart;
+import org.junit.Test;
+
+/**
+ * In this class there is a test that parses big file. It will take a while to execute.
+ */
+public class Mime4jMboxParserImplTest {
+
+    private Mime4jMboxParserImpl parser = new Mime4jMboxParserImpl();
+
+    private static final String TEST_FOLDER = Mime4jMboxParserImplCountTest.TEST_FOLDER;
+    private static final String WRONGBODY_MBOX = "wrongbody.mbox";
+
+    @Test
+    public void testMboxParsing() throws IOException {
+        final String testPath = TEST_FOLDER + WRONGBODY_MBOX;
+        Iterator<Message> iter = parser.parse(new FileInputStream(new File(testPath)));
+
+        boolean fail = true;
+        int i = 1;
+        while (iter.hasNext()) {
+            final Message message = iter.next();
+            File bodyFile = new File(specialPathFromFilePath(testPath, "_bodyOf" + i, "txt"));
+            if (bodyFile.exists()) {
+                final String actual = getPlainBody(message);
+                final String expected = readTextFile(bodyFile);
+                assertEquals("Body #"+i, expected, actual);
+                fail = false;
+            }
+            i++;
+        }
+
+        if (fail) {
+            fail("No file with expected body.");
+        }
+    }
+
+    /**
+     *        code taken from http://www.mozgoweb.com/posts/how-to-parse-mime-message-using-mime4j-library/
+     */
+    private static String getPlainBody(Message msg) throws IOException {
+        if (!msg.isMultipart()) {
+            return getTextPart(msg);
+        } else {
+            Multipart multipart = (Multipart) msg.getBody();
+            for (Entity enitiy : multipart.getBodyParts()) {
+                BodyPart part = (BodyPart) enitiy;
+                if (part.isMimeType("text/plain")) {
+                    return getTextPart(part);
+                }
+            }
+        }
+
+        return null;
+    }
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/SearchServiceTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/SearchServiceTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/SearchServiceTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/SearchServiceTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,111 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SearchServiceTest {
+
+	private static SearchQueryParserImpl parser = new SearchQueryParserImpl();
+	private static QueryBuilderImpl builder = new QueryBuilderImpl();
+
+	private String searchPhrase;
+	private String expectedQuery;
+
+	@Parameters(name="{0}")
+	public static Collection<Object[]> data() {
+		List<Object[]> params = new ArrayList<Object[]>();
+		params.add(new Object[] {"blank search field", "", QueryBuilderImpl.BASE } );
+
+		params.add(new Object[] {"one word", "word", QueryBuilderImpl.BASE + " AND "
+				+ "(LOWER(Body) LIKE '%word%' "
+				+ "OR LOWER(Subject) LIKE '%word%' "
+				+ "OR LOWER('List-Id') LIKE '%word%' "
+				+ "OR LOWER(From) LIKE '%word%')" 
+		} );	
+		
+		// TODO logically this is true, practically order by score because
+		params.add(new Object[] {"two words", "hello word", QueryBuilderImpl.BASE + " AND " 
+				+ "(LOWER(Body) LIKE '%hello%' "
+				+ "OR LOWER(Body) LIKE '%word%' "
+				+ "OR LOWER(Subject) LIKE '%hello%' "
+				+ "OR LOWER(Subject) LIKE '%word%' "
+				+ "OR LOWER('List-Id') LIKE '%hello%' "
+				+ "OR LOWER('List-Id') LIKE '%word%' "
+				+ "OR LOWER(From) LIKE '%hello%' " 
+				+ "OR LOWER(From) LIKE '%word%')" 
+		} );	
+
+		params.add(new Object[] {"field search", "hello from:world", QueryBuilderImpl.BASE + " AND "
+				+ "(LOWER(From) LIKE '%world%') "
+				+ "AND (LOWER(Body) LIKE '%hello%' "
+				+ "OR LOWER(Subject) LIKE '%hello%' "
+				+ "OR LOWER('List-Id') LIKE '%hello%' "
+				+ "OR LOWER(From) LIKE '%hello%')" 
+		} );
+
+		params.add(new Object[] {"caps", "SuBjecT:HeRE THeRe", QueryBuilderImpl.BASE + " AND "
+				+ "(LOWER(Subject) LIKE '%here%') " 
+				+ "AND (LOWER(Body) LIKE '%there%' "
+				+ "OR LOWER(Subject) LIKE '%there%' "
+				+ "OR LOWER('List-Id') LIKE '%there%' "
+				+ "OR LOWER(From) LIKE '%there%')" 
+		} );
+
+		params.add(new Object[] {"non-existent field", "FROM:me list:public about:stuff", QueryBuilderImpl.BASE + " AND " 
+				+ "(LOWER('List-Id') LIKE '%public%') "
+				+ "AND (LOWER(From) LIKE '%me%')" 
+		} );
+
+		params.add(new Object[] {"just non-existent field", "frome:e", QueryBuilderImpl.DUMMY } );
+
+		params.add(new Object[] {"two spaces (parsing)", "a  b", QueryBuilderImpl.BASE + " AND "
+				+ "(LOWER(Body) LIKE '%a%' "
+				+ "OR LOWER(Body) LIKE '%b%' "
+				+ "OR LOWER(Subject) LIKE '%a%' "
+				+ "OR LOWER(Subject) LIKE '%b%' "
+				+ "OR LOWER('List-Id') LIKE '%a%' "
+				+ "OR LOWER('List-Id') LIKE '%b%' "
+				+ "OR LOWER(From) LIKE '%a%' " 
+				+ "OR LOWER(From) LIKE '%b%')" 
+		} );	
+
+		params.add(new Object[] {"quoted text", "\"hel  wrd\"", QueryBuilderImpl.BASE + " AND "
+				+ "(LOWER(Body) LIKE '%hel  wrd%' "
+				+ "OR LOWER(Subject) LIKE '%hel  wrd%' "
+				+ "OR LOWER('List-Id') LIKE '%hel  wrd%' "
+				+ "OR LOWER(From) LIKE '%hel  wrd%')" 
+		} );	
+		
+		params.add(new Object[] {"quoted field", "from:\"w r d\" hello ", QueryBuilderImpl.BASE + " AND "
+				+ "(LOWER(From) LIKE '%w r d%') "
+				+ "AND (LOWER(Body) LIKE '%hello%' "
+				+ "OR LOWER(Subject) LIKE '%hello%' "
+				+ "OR LOWER('List-Id') LIKE '%hello%' "
+				+ "OR LOWER(From) LIKE '%hello%')" 
+		} );
+
+		//        params.add(new Object[] {"name", "", QueryBuilderImpl.BASE} );
+		return params;
+	}
+
+	public SearchServiceTest(String description, String one, String two) {
+		searchPhrase = one;
+		expectedQuery = two;
+	}
+
+	@Test
+	public void testQueryPipeline() {
+		String query = builder.buildQuery(parser.parse(searchPhrase), QueryBuilderImpl.SQL2);
+		assertTrue(String.format("\nExpected: %s\n   Output: %s", expectedQuery, query), expectedQuery.equals(query));
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplSubjectEqualityTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplSubjectEqualityTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplSubjectEqualityTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplSubjectEqualityTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,41 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ThreadKeyGeneratorImplSubjectEqualityTest {
+    private ThreadKeyGeneratorImpl generator = new ThreadKeyGeneratorImpl();
+    private final String orig;
+    private final String re;
+
+    public ThreadKeyGeneratorImplSubjectEqualityTest(String input, String expected) {
+        this.orig = input;
+        this.re = expected;
+    }
+
+    @Parameters(name="{0}")
+    public static List<Object[]> data() {
+        final List<Object[]> result = new ArrayList<Object[]>();
+
+        result.add(new Object[] {"Chef cookbooks for Installing CQ & packages", "Re: Chef cookbooks for Installing CQ & packages"} ); 
+        result.add(new Object[] {"Dropbox to throw random files in and be accessible through http/ ftp?", "Re: Dropbox to throw random files in and be accessible through http/ ftp?"} ); 
+        result.add(new Object[] {"Dropbox to throw random files in and be accessible through http/ ftp?", "RE: Dropbox to throw random files in and be accessible through http/ ftp?"} ); 
+        result.add(new Object[] {"CRX integration guidelines for ES3", "答复: CRX integration guidelines for ES3"} ); 
+        //        result.add(new Object[] {, } ); 
+
+        return result;
+    }
+
+    @Test
+    public void testGetThreadKey() {
+        assertEquals(generator.getThreadKey(orig), generator.getThreadKey(re));
+    }
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplTest.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplTest.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplTest.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,54 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ThreadKeyGeneratorImplTest {
+	private static final String UNADDRESSABLE_SUBJECT_KEY = "a/at/unaddressable_subject";
+    private ThreadKeyGeneratorImpl generator = new ThreadKeyGeneratorImpl();
+	private final String input;
+	private final String expected;
+
+	@Parameters(name="{0}")
+	public static List<Object[]> data() {
+		final List<Object[]> result = new ArrayList<Object[]>();
+		
+		result.add(new Object[] {"'''''''9>*'''''''''''''''''''''''''''40>*", "9/90/940"} ); 
+		result.add(new Object[] {"'abc'''9>*'''''''''''''''''''''''''''40>*", "9/90/abc940"} ); 
+		result.add(new Object[] {"abcdefg9>*'''''''''''''''''''''''''''40>*", "9/90/abcdefg940"} ); 
+		result.add(new Object[] {"abcdefg9>h'''''''''''''''''''''''''''40>*", "h/h0/abcdefg9h40"} ); 
+		result.add(new Object[] {"abcdefg9>hi''''''''''''''''''''''''''40>*", "h/h0/abcdefg9hi40"} ); 
+		result.add(new Object[] {"abcdefg9>hijklmnopqrstuvwxyzabcdefghi40>*", "h/h0/abcdefg9hijklmnopqrstuvwxyzabcdefghi40"} ); 
+		result.add(new Object[] {"abcdefg9>hijklmnopqrstuvwxyzabcdefghi40>j", "h/hj/abcdefg9hijklmnopqrstuvwxyzabcdefghi40j"} ); 
+		result.add(new Object[] {"abcdefg9>hijklmnopqrstuvwxyzabcdefghi40>jk","h/hj/abcdefg9hijklmnopqrstuvwxyzabcdefghi40jk"} ); 
+		result.add(new Object[] {"'''''''9>'''''''abc''''''''''''''''''40>*", "9/90/9abc40"} ); 
+		result.add(new Object[] {"'''''''9>*'''''''''''''''''''''''''''40>*abc'", "9/90/940abc"} ); 
+		result.add(new Object[] {"", UNADDRESSABLE_SUBJECT_KEY} ); 
+		result.add(new Object[] {"Re: ", UNADDRESSABLE_SUBJECT_KEY} ); 
+		result.add(new Object[] {null, UNADDRESSABLE_SUBJECT_KEY} ); 
+		result.add(new Object[] {"*", UNADDRESSABLE_SUBJECT_KEY} ); 
+		result.add(new Object[] {"1.5.0", "0/00/1_5_0"} ); 
+		result.add(new Object[] {"把握正确方向,做个效率为先的领导助手", UNADDRESSABLE_SUBJECT_KEY} ); 
+		result.add(new Object[] {"remove   consecutive - . - whitespaces", "c/cs/remove_consecutive_whitespaces"} ); 
+
+		return result;
+	}
+
+	public ThreadKeyGeneratorImplTest(String input, String expected) {
+		this.input = input;
+		this.expected = expected;
+	}
+
+	@Test
+	public void testGetThreadKey() {
+		assertEquals(expected, generator.getThreadKey(input));
+	}
+}
\ No newline at end of file

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SearchSandbox.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SearchSandbox.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SearchSandbox.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SearchSandbox.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,93 @@
+package org.apache.sling.mailarchiveserver.util;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Scanner;
+
+import javax.jcr.query.Query;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.sling.api.resource.LoginException;
+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.ResourceResolverFactory;
+
+/**
+ * Helper REPL for querying JCR with SQL2 or XPath
+ * 
+ * @author bogomolo
+ */
+
+@Component
+public class SearchSandbox {
+
+	@Reference
+	private	ResourceResolverFactory resourceResolverFactory;
+	ResourceResolver resolver = null;
+
+	@Activate
+	public void test() throws PersistenceException, LoginException {
+		System.out.println("*** Search service sandbox");
+
+		if (resolver == null) {
+			resolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
+		}
+
+		Resource testRoot;
+
+		final Resource root = resolver.getResource("/");
+		final String path = getClass().getSimpleName();
+		testRoot = resolver.getResource(root, path);
+		if (testRoot == null) {
+			testRoot = resolver.create(root, path, null);
+			resolver.commit();
+		}
+		
+		for (int i = 0; i < 3; i++) {
+			final Props props = new Props("title", "HELLO", "jcr:text", "world");
+			resolver.create(testRoot, "child_" + System.currentTimeMillis(), props).getPath();
+			resolver.commit();
+		}
+
+		Scanner sc = new Scanner(System.in);
+		System.out.println("Type \"quit\" to continue loading MAS.");
+		System.out.print("*** New query: >");
+		String query = sc.nextLine();
+		while (!query.equalsIgnoreCase("quit")) {
+			try {
+				System.out.println("*** sql");
+				Iterator<Resource> resSQL = resolver.findResources(query, Query.JCR_SQL2);
+				while (resSQL.hasNext()) {
+					Resource resource = (Resource) resSQL.next();
+					System.out.println(resource.toString());
+				}
+				
+//				System.out.println("*** xpath");
+//				Iterator<Resource> resXPath = resolver.findResources(query, Query.XPATH);
+//				while (resXPath.hasNext()) {
+//					Resource resource = (Resource) resXPath.next();
+//					System.out.println(resource.toString());
+//				}
+
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+			
+			System.out.print("*** New query: >");
+			query = sc.nextLine();
+		}
+	}
+
+	@SuppressWarnings("serial")
+	private static class Props extends HashMap<String, Object> {
+		Props(String ... keyValue) {
+			for(int i=0 ; i< keyValue.length; i+=2) {
+				put(keyValue[i], keyValue[i+1]);
+			}
+		}
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SubjectLettersEntropy.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SubjectLettersEntropy.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SubjectLettersEntropy.java (added)
+++ sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SubjectLettersEntropy.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,104 @@
+package org.apache.sling.mailarchiveserver.util;
+
+import java.io.PrintStream;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.ValueMap;
+
+/**
+ * Util class to calculate entropy of a letter position in the message subject.
+ * 
+ * @author bogomolo
+ */
+
+@Component
+public class SubjectLettersEntropy {
+
+	private static final int SAMPLE_LENGTH = 300;
+	private static final int ALPHABET_LENGTH = 26;
+	private static final double THRESHOLD = 0.9;
+
+	private static final double MAX_THEOR_ENTROPY = -Math.log10(1./ALPHABET_LENGTH);
+
+	@Reference
+	ResourceResolverFactory resourceResolverFactory;
+	private ResourceResolver resolver = null;
+	public static SubjectLettersEntropy instance = null;
+
+	public SubjectLettersEntropy() {
+		if (instance == null) {
+			instance = this;
+		} 
+	}
+
+	int[][] count = new int[SAMPLE_LENGTH][ALPHABET_LENGTH];
+	double[] entropy = new double[SAMPLE_LENGTH];
+	int messages = 0;
+
+	public void calculateEntropyAndPrint(PrintStream out) {
+		try {
+			if (resourceResolverFactory == null) {
+				System.out.println("resourceResolverFactory is NULL");
+			}
+			resolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
+
+			String root = "/content/mailarchiveserver/archive"; // /domain/project/list/t/th/thread/message
+			Resource main = resolver.getResource(root);
+			iterate(main, 6);
+			int[] sum = new int[SAMPLE_LENGTH];
+
+			for (int i = 0; i < sum.length; i++) {
+				for (int j = 0; j < ALPHABET_LENGTH; j++) {
+					sum[i] += count[i][j];
+				}
+			}
+
+			for (int i = 0; i < sum.length; i++) {
+				for (int j = 0; j < ALPHABET_LENGTH; j++) {
+					if (count[i][j] > 0) {
+						double num = count[i][j]/1./sum[i];
+						entropy[i] += - num * Math.log10(num); 
+					}
+				}
+			}
+
+			System.out.println(String.format("%s\t%s\t%s", "charAt","entropy", "sum"));
+			for (int i = 0; i < sum.length; i++) {
+				if (entropy[i] >= MAX_THEOR_ENTROPY*THRESHOLD || sum[i] >= messages*THRESHOLD) 
+					System.out.println(String.format("%d\t%.3f\t%d", i, entropy[i], sum[i]));
+			}
+			out.println("Messages #: "+messages);
+			
+		} catch (LoginException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void countLetters(Resource r) {
+		messages++;
+		ValueMap properties = r.adaptTo(ValueMap.class);
+		String subj = properties.get("Subject", (String) null);
+		for (int i = 0; i < Math.min(subj.length(), SAMPLE_LENGTH); i++) {
+			Character c = Character.toLowerCase(subj.charAt(i));
+			if (c.toString().matches("[a-z]")) {
+				count[i][c-'a']++;
+			}
+		}
+	}
+
+	private void iterate(Resource r, int lvl) {
+		for (Resource child : r.getChildren()) {
+			if (lvl == 0) {
+				countLetters(child);
+			} else {
+				iterate(child, lvl-1);
+			}
+		}
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/test_files/multipart.txt
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/test_files/multipart.txt?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/test_files/multipart.txt (added)
+++ sling/trunk/samples/mail-archive/server/test_files/multipart.txt Mon Dec 23 11:11:15 2013
@@ -0,0 +1,109 @@
+Return-Path: <common-dev-return-77410-apmail-hadoop-common-dev-archive=hadoop.apache.org@hadoop.apache.org>
+X-Original-To: apmail-hadoop-common-dev-archive@www.apache.org
+Delivered-To: apmail-hadoop-common-dev-archive@www.apache.org
+Received: from mail.apache.org (hermes.apache.org [140.211.11.3])
+	by minotaur.apache.org (Postfix) with SMTP id 275929C5F
+	for <apmail-hadoop-common-dev-archive@www.apache.org>; Sun,  1 Jan 2012 05:31:21 +0000 (UTC)
+Received: (qmail 5467 invoked by uid 500); 1 Jan 2012 05:31:20 -0000
+Delivered-To: apmail-hadoop-common-dev-archive@hadoop.apache.org
+Received: (qmail 4867 invoked by uid 500); 1 Jan 2012 05:31:08 -0000
+Mailing-List: contact common-dev-help@hadoop.apache.org; run by ezmlm
+Precedence: bulk
+List-Help: <mailto:common-dev-help@hadoop.apache.org>
+List-Unsubscribe: <mailto:common-dev-unsubscribe@hadoop.apache.org>
+List-Post: <mailto:common-dev@hadoop.apache.org>
+List-Id: <common-dev.hadoop.apache.org>
+Reply-To: common-dev@hadoop.apache.org
+Delivered-To: mailing list common-dev@hadoop.apache.org
+Received: (qmail 4859 invoked by uid 99); 1 Jan 2012 05:31:05 -0000
+Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136)
+    by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 01 Jan 2012 05:31:05 +0000
+X-ASF-Spam-Status: No, hits=1.5 required=5.0
+	tests=HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_PASS
+X-Spam-Check-By: apache.org
+Received-SPF: pass (athena.apache.org: domain of ronald.petty@gmail.com designates 74.125.82.176 as permitted sender)
+Received: from [74.125.82.176] (HELO mail-we0-f176.google.com) (74.125.82.176)
+    by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 01 Jan 2012 05:31:00 +0000
+Received: by werm10 with SMTP id m10so10570169wer.35
+        for <common-dev@hadoop.apache.org>; Sat, 31 Dec 2011 21:30:39 -0800 (PST)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+        d=gmail.com; s=gamma;
+        h=mime-version:from:date:message-id:subject:to:content-type;
+        bh=zbylm9gc+1ZZdrKyuZCh2myzsS2W9C01CCAV9oo6vkA=;
+        b=czzeM+s45qAT+QBYb+swdHIsKQUHn1mocaeE0Jd4ZbBZfepTDA3IjAgX0QPxeibfOx
+         phTYdcY0ZaqeOoCVxIrwlZs0vfwF5FkH33yOIIJ9mJ1iGxFHtWTlYWDTrSDkVRhH6Jgj
+         BWMpsbhh5I8p56G9zhKI0xS4joa6Wssnq83WQ=
+Received: by 10.216.138.101 with SMTP id z79mr31006394wei.7.1325395838866;
+ Sat, 31 Dec 2011 21:30:38 -0800 (PST)
+MIME-Version: 1.0
+Received: by 10.216.6.27 with HTTP; Sat, 31 Dec 2011 21:30:17 -0800 (PST)
+From: Ronald Petty <ronald.petty@gmail.com>
+Date: Sun, 1 Jan 2012 00:30:17 -0500
+Message-ID: <CA+mbt7jXoOyp+zXFV2GEyVGTqDQ4atxVKGmec5gfoVhP=32bRw@mail.gmail.com>
+Subject: Building Trunk on EC2
+To: common-dev@hadoop.apache.org
+Content-Type: multipart/alternative; boundary=0016e6d6417629976904b570c4d2
+
+--0016e6d6417629976904b570c4d2
+Content-Type: text/plain; charset=ISO-8859-1
+
+Hello,
+
+If anyone is interested, here are my notes on how to build trunk on bare
+bones AWS EC2 instance.
+
+   1. Create a XL 64-bit AWS EC2 instance (anything smaller fails due to
+   lack of RAM)
+   2. SSH to EC2 instance
+   3. sudo useradd -u 1000 hadoop
+   4. sudo yum install svn gcc.x86_64 gcc-c++.x86_64 libtool.x86_64
+   make.x86_64 automake.noarch autoconf.noarch java-1.6.0-openjdk-devel.x86_64
+   zlib-devel.x86_64
+   5. set JAVA_HOME in vi /etc/profile.d/aws-apitools-common.sh to
+   /usr/lib/jvm/java  (if you don't the native components will not build due
+   to using an incorrect JAVA_HOME even if you set evn JAVA_HOME)
+   6. reboot
+   7. login
+   8. su hadoop
+   9. sudo mkdir /data
+   10. sudo chown -R hadoop:hadoop /data
+   11. cd /data
+   12. wget http://apache.petsads.us//forrest/apache-forrest-0.9.tar.gz
+   13. wget
+   http://mirror.cc.columbia.edu/pub/software/apache//maven/binaries/apache-maven-3.0.3-bin.tar.gz
+   14. wget http://protobuf.googlecode.com/files/protobuf-2.4.1.tar.gz
+   15. wget
+   http://superb-sea2.dl.sourceforge.net/project/findbugs/findbugs/2.0.0/findbugs-2.0.0.tar.gz
+   16. gunzip *.gz
+   17. tar xf apache-forrest-0.9.tar
+   18. tar xf apache-maven-3.0.3-bin.tar
+   19. tar xf findbugs-2.0.0.tar
+   20. tar xf protobuf-2.4.1.tar
+   21. export FORREST_HOME=/data/apache-forrest-0.9
+   22. export MAVEN_HOME=/data/apache-maven-3.0.3
+   23. export FINDBUGS_HOME=/data/findbugs-2.0.0
+   24. cd protobuf-2.4.1
+   25. ./configure
+   26. make
+   27. sudo make install
+   28. cd ..
+   29. export
+   PATH=/usr/local/bin/:/data/apache-maven-3.0.3/bin/:/data/apache-forrest-0.9/bin/:/data/findbugs-2.0.0/bin/:$PATH
+   30. svn checkout
+http://svn.apache.org/repos/asf/hadoop/common/trunk/hadoop-trunk
+   31. cd hadoop-trunk
+   32. mvn package -Pdist,native,docs -DskipTests -Dtar
+
+I have not actually used the constructed tar yet, but wanted to share my
+notes.  I did notice two things missing (compared to the regular pre-yarn
+releases), there is no conf directory, nor example jars.  I know trunk is
+being worked on but I am not sure what the plan is there (or if I simply
+missed something.)
+
+If you have any comments, questions, or advice I love to here it.
+
+Kindest regards.
+
+Ron
+
+--0016e6d6417629976904b570c4d2--

Added: sling/trunk/samples/mail-archive/server/test_files/multipart_body.txt
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/test_files/multipart_body.txt?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/test_files/multipart_body.txt (added)
+++ sling/trunk/samples/mail-archive/server/test_files/multipart_body.txt Mon Dec 23 11:11:15 2013
@@ -0,0 +1,59 @@
+Hello,
+
+If anyone is interested, here are my notes on how to build trunk on bare
+bones AWS EC2 instance.
+
+   1. Create a XL 64-bit AWS EC2 instance (anything smaller fails due to
+   lack of RAM)
+   2. SSH to EC2 instance
+   3. sudo useradd -u 1000 hadoop
+   4. sudo yum install svn gcc.x86_64 gcc-c++.x86_64 libtool.x86_64
+   make.x86_64 automake.noarch autoconf.noarch java-1.6.0-openjdk-devel.x86_64
+   zlib-devel.x86_64
+   5. set JAVA_HOME in vi /etc/profile.d/aws-apitools-common.sh to
+   /usr/lib/jvm/java  (if you don't the native components will not build due
+   to using an incorrect JAVA_HOME even if you set evn JAVA_HOME)
+   6. reboot
+   7. login
+   8. su hadoop
+   9. sudo mkdir /data
+   10. sudo chown -R hadoop:hadoop /data
+   11. cd /data
+   12. wget http://apache.petsads.us//forrest/apache-forrest-0.9.tar.gz
+   13. wget
+   http://mirror.cc.columbia.edu/pub/software/apache//maven/binaries/apache-maven-3.0.3-bin.tar.gz
+   14. wget http://protobuf.googlecode.com/files/protobuf-2.4.1.tar.gz
+   15. wget
+   http://superb-sea2.dl.sourceforge.net/project/findbugs/findbugs/2.0.0/findbugs-2.0.0.tar.gz
+   16. gunzip *.gz
+   17. tar xf apache-forrest-0.9.tar
+   18. tar xf apache-maven-3.0.3-bin.tar
+   19. tar xf findbugs-2.0.0.tar
+   20. tar xf protobuf-2.4.1.tar
+   21. export FORREST_HOME=/data/apache-forrest-0.9
+   22. export MAVEN_HOME=/data/apache-maven-3.0.3
+   23. export FINDBUGS_HOME=/data/findbugs-2.0.0
+   24. cd protobuf-2.4.1
+   25. ./configure
+   26. make
+   27. sudo make install
+   28. cd ..
+   29. export
+   PATH=/usr/local/bin/:/data/apache-maven-3.0.3/bin/:/data/apache-forrest-0.9/bin/:/data/findbugs-2.0.0/bin/:$PATH
+   30. svn checkout
+http://svn.apache.org/repos/asf/hadoop/common/trunk/hadoop-trunk
+   31. cd hadoop-trunk
+   32. mvn package -Pdist,native,docs -DskipTests -Dtar
+
+I have not actually used the constructed tar yet, but wanted to share my
+notes.  I did notice two things missing (compared to the regular pre-yarn
+releases), there is no conf directory, nor example jars.  I know trunk is
+being worked on but I am not sure what the plan is there (or if I simply
+missed something.)
+
+If you have any comments, questions, or advice I love to here it.
+
+Kindest regards.
+
+Ron
+

Added: sling/trunk/samples/mail-archive/server/test_files/singlepart.txt
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/test_files/singlepart.txt?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/test_files/singlepart.txt (added)
+++ sling/trunk/samples/mail-archive/server/test_files/singlepart.txt Mon Dec 23 11:11:15 2013
@@ -0,0 +1,66 @@
+Return-Path: <common-dev-return-77583-apmail-hadoop-common-dev-archive=hadoop.apache.org@hadoop.apache.org>
+X-Original-To: apmail-hadoop-common-dev-archive@www.apache.org
+Delivered-To: apmail-hadoop-common-dev-archive@www.apache.org
+Received: from mail.apache.org (hermes.apache.org [140.211.11.3])
+	by minotaur.apache.org (Postfix) with SMTP id 04D6296E1
+	for <apmail-hadoop-common-dev-archive@www.apache.org>; Wed,  1 Feb 2012 00:23:25 +0000 (UTC)
+Received: (qmail 97997 invoked by uid 500); 1 Feb 2012 00:23:21 -0000
+Delivered-To: apmail-hadoop-common-dev-archive@hadoop.apache.org
+Received: (qmail 97504 invoked by uid 500); 1 Feb 2012 00:23:20 -0000
+Mailing-List: contact common-dev-help@hadoop.apache.org; run by ezmlm
+Precedence: bulk
+List-Help: <mailto:common-dev-help@hadoop.apache.org>
+List-Unsubscribe: <mailto:common-dev-unsubscribe@hadoop.apache.org>
+List-Post: <mailto:common-dev@hadoop.apache.org>
+List-Id: <common-dev.hadoop.apache.org>
+Reply-To: common-dev@hadoop.apache.org
+Delivered-To: mailing list common-dev@hadoop.apache.org
+Received: (qmail 97483 invoked by uid 99); 1 Feb 2012 00:23:20 -0000
+Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230)
+    by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 01 Feb 2012 00:23:20 +0000
+X-ASF-Spam-Status: No, hits=-2000.0 required=5.0
+	tests=ALL_TRUSTED,T_RP_MATCHES_RCVD
+X-Spam-Check-By: apache.org
+Received: from [140.211.11.116] (HELO hel.zones.apache.org) (140.211.11.116)
+    by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 01 Feb 2012 00:23:17 +0000
+Received: from hel.zones.apache.org (hel.zones.apache.org [140.211.11.116])
+	by hel.zones.apache.org (Postfix) with ESMTP id CF2F31679FA
+	for <common-dev@hadoop.apache.org>; Wed,  1 Feb 2012 00:22:56 +0000 (UTC)
+Date: Wed, 1 Feb 2012 00:22:56 +0000 (UTC)
+From: "Roman Shaposhnik (Created) (JIRA)" <jira@apache.org>
+To: common-dev@hadoop.apache.org
+Message-ID: <250130243.649.1328055776849.JavaMail.tomcat@hel.zones.apache.org>
+Subject: [jira] [Created] (HADOOP-8010) hadoop-config.sh spews error message
+ when HADOOP_HOME_WARN_SUPPRESS is set to true and HADOOP_HOME is present
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 7bit
+X-JIRA-FingerPrint: 30527f35849b9dde25b450d4833f0394
+X-Virus-Checked: Checked by ClamAV on apache.org
+
+hadoop-config.sh spews error message when HADOOP_HOME_WARN_SUPPRESS is set to true and HADOOP_HOME is present
+-------------------------------------------------------------------------------------------------------------
+
+                 Key: HADOOP-8010
+                 URL: https://issues.apache.org/jira/browse/HADOOP-8010
+             Project: Hadoop Common
+          Issue Type: Bug
+          Components: scripts
+    Affects Versions: 1.0.0
+            Reporter: Roman Shaposhnik
+            Assignee: Roman Shaposhnik
+            Priority: Minor
+             Fix For: 1.0.1
+
+
+Running hadoop daemon commands when HADOOP_HOME_WARN_SUPPRESS is set to true and HADOOP_HOME is present produces:
+{noformat}
+  [: 76: true: unexpected operator
+{noformat}
+
+--
+This message is automatically generated by JIRA.
+If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
+For more information on JIRA, see: http://www.atlassian.com/software/jira
+
+        

Added: sling/trunk/samples/mail-archive/server/test_files/singlepart_body.txt
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/test_files/singlepart_body.txt?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/test_files/singlepart_body.txt (added)
+++ sling/trunk/samples/mail-archive/server/test_files/singlepart_body.txt Mon Dec 23 11:11:15 2013
@@ -0,0 +1,27 @@
+hadoop-config.sh spews error message when HADOOP_HOME_WARN_SUPPRESS is set to true and HADOOP_HOME is present
+-------------------------------------------------------------------------------------------------------------
+
+                 Key: HADOOP-8010
+                 URL: https://issues.apache.org/jira/browse/HADOOP-8010
+             Project: Hadoop Common
+          Issue Type: Bug
+          Components: scripts
+    Affects Versions: 1.0.0
+            Reporter: Roman Shaposhnik
+            Assignee: Roman Shaposhnik
+            Priority: Minor
+             Fix For: 1.0.1
+
+
+Running hadoop daemon commands when HADOOP_HOME_WARN_SUPPRESS is set to true and HADOOP_HOME is present produces:
+{noformat}
+  [: 76: true: unexpected operator
+{noformat}
+
+--
+This message is automatically generated by JIRA.
+If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
+For more information on JIRA, see: http://www.atlassian.com/software/jira
+
+        
+



Mime
View raw message