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 [1/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
Author: bdelacretaz
Date: Mon Dec 23 11:11:15 2013
New Revision: 1553094

URL: http://svn.apache.org/r1553094
Log:
SLING-3297 - mail archive server sample app, contributed by Igor Bogomolov, thanks!

Added:
    sling/trunk/samples/mail-archive/   (with props)
    sling/trunk/samples/mail-archive/docs/
    sling/trunk/samples/mail-archive/docs/sling-mail-archive-server-specs.md
    sling/trunk/samples/mail-archive/pom.xml
    sling/trunk/samples/mail-archive/server/   (with props)
    sling/trunk/samples/mail-archive/server/pom.xml
    sling/trunk/samples/mail-archive/server/src/
    sling/trunk/samples/mail-archive/server/src/main/
    sling/trunk/samples/mail-archive/server/src/main/java/
    sling/trunk/samples/mail-archive/server/src/main/java/org/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/AttachmentFilter.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/Connector.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MboxParser.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageProcessor.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageStore.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/QueryBuilder.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchQueryParser.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchService.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/ThreadKeyGenerator.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/AttachmentFilterImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ConnectorScheduler.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ImportMboxServlet.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/QueryBuilderImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchQueryParserImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchServiceImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImpl.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MailArchiveServerConstants.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MessageFieldName.java
    sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/SysStreamsLogger.java
    sling/trunk/samples/mail-archive/server/src/main/resources/
    sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/
    sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/content/
    sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/content/mailarchiveserver.json
    sling/trunk/samples/mail-archive/server/src/test/
    sling/trunk/samples/mail-archive/server/src/test/java/
    sling/trunk/samples/mail-archive/server/src/test/java/org/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplAttachmentsTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplRepositoryTestUtil.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImplStaticMethodsTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplCountTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplStreamingTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImplTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/SearchServiceTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplSubjectEqualityTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImplTest.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SearchSandbox.java
    sling/trunk/samples/mail-archive/server/src/test/java/org/apache/sling/mailarchiveserver/util/SubjectLettersEntropy.java
    sling/trunk/samples/mail-archive/server/test_files/
    sling/trunk/samples/mail-archive/server/test_files/multipart.txt
    sling/trunk/samples/mail-archive/server/test_files/multipart_body.txt
    sling/trunk/samples/mail-archive/server/test_files/singlepart.txt
    sling/trunk/samples/mail-archive/server/test_files/singlepart_body.txt
    sling/trunk/samples/mail-archive/server/test_files/singlepart_headers.txt
    sling/trunk/samples/mail-archive/server/test_files/three_messages.mbox
    sling/trunk/samples/mail-archive/server/test_files/wrongbody.mbox
    sling/trunk/samples/mail-archive/server/test_files/wrongbody.txt
    sling/trunk/samples/mail-archive/server/test_files/wrongbody_body.txt
    sling/trunk/samples/mail-archive/server/test_files/wrongbody_bodyOf1.txt
    sling/trunk/samples/mail-archive/server/test_files/wrongbody_bodyOf4.txt
    sling/trunk/samples/mail-archive/server/test_files/wrongbody_bodyOf5.txt
    sling/trunk/samples/mail-archive/ui/   (with props)
    sling/trunk/samples/mail-archive/ui/pom.xml
    sling/trunk/samples/mail-archive/ui/src/
    sling/trunk/samples/mail-archive/ui/src/main/
    sling/trunk/samples/mail-archive/ui/src/main/resources/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/archive/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/archive/html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/head.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/import/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/import/mbox.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/list/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/list/html.esp   (with props)
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/message/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/message/html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/message/preview.html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/navbar.html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/root/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/root/html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/search/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/search/html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/styles.css
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/thread/
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/thread/html.esp
    sling/trunk/samples/mail-archive/ui/src/main/resources/initial-content/apps/mailarchiveserver/util.js

Propchange: sling/trunk/samples/mail-archive/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Dec 23 11:11:15 2013
@@ -0,0 +1,13 @@
+target
+bin
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+
+
+

Added: sling/trunk/samples/mail-archive/docs/sling-mail-archive-server-specs.md
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/docs/sling-mail-archive-server-specs.md?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/docs/sling-mail-archive-server-specs.md (added)
+++ sling/trunk/samples/mail-archive/docs/sling-mail-archive-server-specs.md Mon Dec 23 11:11:15 2013
@@ -0,0 +1,42 @@
+Sling Mail Archive Server Specification
+=======================================
+
+Overview
+--------
+The goal of this student project is to create a mail archive server
+based on Apache Sling.
+
+The server is meant both as a high capacity archive server for mailing lists,
+as well as an example application that demonstrates Sling best practices.
+
+This is an initial specification for the server, we'll refine it as we go.
+
+Use cases
+---------
+Here's a rough initial list of use cases:
+
+* Import email live from POP/IMAP servers, as well as via Exchange web services
+* Mass imports of various mail archive file formats: mbox, Outlook PST, etc.
+* Provide time-based and thread-based mail browsing RESTful and web interfaces.
+* Provide short permanent URLs for each individual message and each thread.
+* Optional content filters to collapse repeated sections in messages (quoting other messages).
+* Optional content filters for intelligent linking (to jira etc.)
+* Optional content filters for "de-junking" Outlook messages.
+* Optional content filters for syntax coloring of code excerpts.
+* Optional exclude filters to ignore some messages (Jenkins notifications for instance)
+* Tagging of archived messages, for example to flag them as useful or obsolete. 
+* Tag-based navigation.
+* Full-text and structured search.
+* Traffic statistics.
+
+Architecture, performance, scalability etc.
+-------------------------------------------
+Also a rough list of criteria for now:
+
+* Use Sling for all web interactions, Jackrabbit Oak as the content repository once that becomes available, jQuery etc. for the front-end (or maybe http://createjs.org/)
+* OSGi services for everything, some scripting for HTML rendering and user plugins if appropriate
+* Kill -9 is fine as a shutdown mechanism, causes no harm
+* Handle huge import files, avoid loading the whole thing in memory to stay scalable
+* Scale to thousands of lists and millions of messages
+* Much less than 2 seconds response time for all common operations
+* Optional per-list access control

Added: sling/trunk/samples/mail-archive/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/pom.xml?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/pom.xml (added)
+++ sling/trunk/samples/mail-archive/pom.xml Mon Dec 23 11:11:15 2013
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.sling</groupId>
+    <artifactId>org.apache.sling.mailarchive.reactor</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>Apache Sling Archive Server Reactor</name>
+
+    <modules>
+        <module>server</module>
+        <module>ui</module>
+    </modules>
+</project>

Propchange: sling/trunk/samples/mail-archive/server/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Dec 23 11:11:15 2013
@@ -0,0 +1,13 @@
+target
+bin
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+
+
+

Added: sling/trunk/samples/mail-archive/server/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/pom.xml?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/pom.xml (added)
+++ sling/trunk/samples/mail-archive/server/pom.xml Mon Dec 23 11:11:15 2013
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>18</version>
+    </parent>
+
+    <groupId>org.apache.sling</groupId>
+    <artifactId>org.apache.sling.mailarchive.server</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>Apache Sling Mail Archive Server</name>
+    <description>Server is designed to archive messages of mailing lists and allows convenient search and navigation through them.</description>
+
+    <properties>
+        <sling.java.version>6</sling.java.version>
+    </properties>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Sling-Initial-Content>initial-content</Sling-Initial-Content>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>0.6.2.201302030002</version>
+                <executions>
+                    <execution>
+                        <id>prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>check</id>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <configuration>
+                            <check>
+								start with low values
+								<classRatio>40</classRatio>
+								<instructionRatio>40</instructionRatio>
+								<methodRatio>40</methodRatio>
+								<branchRatio>40</branchRatio>
+								<complexityRatio>40</complexityRatio>
+								<lineRatio>40</lineRatio>
+							</check>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>apache-mime4j-core</artifactId>
+            <version>0.8.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>apache-mime4j-mbox-iterator</artifactId>
+            <version>0.8.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>apache-mime4j-dom</artifactId>
+            <version>0.8.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>apache-mime4j-storage</artifactId>
+            <version>0.8.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-jcr-commons</artifactId>
+            <version>2.7.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.scheduler</artifactId>
+            <version>2.3.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.jetty</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.4.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.api</artifactId>
+            <version>2.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+            <version>2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+        <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>1.9.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.testing</artifactId>
+            <version>2.0.14</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/AttachmentFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/AttachmentFilter.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/AttachmentFilter.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/AttachmentFilter.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,9 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import org.apache.james.mime4j.message.BodyPart;
+
+public interface AttachmentFilter {
+    
+    boolean isEligible(BodyPart attachment);
+    
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/Connector.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/Connector.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/Connector.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/Connector.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,7 @@
+package org.apache.sling.mailarchiveserver.api;
+
+public interface Connector {
+
+	int checkNewMessages();
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MboxParser.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MboxParser.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MboxParser.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MboxParser.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,13 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import org.apache.james.mime4j.dom.Message;
+
+public interface MboxParser {
+	
+	Iterator<Message> parse(InputStream is) throws IOException;
+	
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageProcessor.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageProcessor.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageProcessor.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageProcessor.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,10 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import org.apache.james.mime4j.dom.Message;
+
+/** MessageProcessors are used by our {@link MessageStore}
+ *  implementation to pre-process messages before storing them.
+ */
+public interface MessageProcessor {
+	void processMessage(Message m);
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageStore.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageStore.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageStore.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/MessageStore.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,14 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.james.mime4j.dom.Message;
+
+public interface MessageStore {
+	
+	void save(Message m) throws IOException;
+	
+	void saveAll(Iterator<Message> iterator) throws IOException;
+	
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/QueryBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/QueryBuilder.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/QueryBuilder.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/QueryBuilder.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,10 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import java.util.List;
+import java.util.Map;
+
+public interface QueryBuilder {
+
+	String buildQuery(Map<String, List<String>> tokens, String lang);
+	
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchQueryParser.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchQueryParser.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchQueryParser.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchQueryParser.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,10 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SearchQueryParser {
+    
+	Map<String, List<String>> parse(String query);
+	
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchService.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchService.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchService.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/SearchService.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,11 @@
+package org.apache.sling.mailarchiveserver.api;
+
+import java.util.Iterator;
+
+import org.apache.sling.api.resource.Resource;
+
+public interface SearchService {
+
+	Iterator<Resource> find(String phrase);
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/ThreadKeyGenerator.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/ThreadKeyGenerator.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/ThreadKeyGenerator.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/api/ThreadKeyGenerator.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,7 @@
+package org.apache.sling.mailarchiveserver.api;
+
+public interface ThreadKeyGenerator {
+
+	String getThreadKey(String subject);
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/AttachmentFilterImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/AttachmentFilterImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/AttachmentFilterImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/AttachmentFilterImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,54 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.james.mime4j.dom.BinaryBody;
+import org.apache.james.mime4j.dom.Body;
+import org.apache.james.mime4j.dom.TextBody;
+import org.apache.james.mime4j.message.BodyPart;
+import org.apache.sling.mailarchiveserver.api.AttachmentFilter;
+
+@Component
+@Service(AttachmentFilter.class)
+public class AttachmentFilterImpl implements AttachmentFilter {
+
+    private Set<String> eligibleExtensions = null;
+    private long maxSize = (long) 5e6; // 5 Mb
+
+    @Override
+    public boolean isEligible(BodyPart attachment) {
+        // extension check
+        final String filename = attachment.getFilename();
+        String ext = "";
+        int idx = filename.lastIndexOf('.');
+        if (idx > -1) {
+            ext = filename.substring(idx + 1);
+        }
+        if (eligibleExtensions != null && !eligibleExtensions.contains(ext)) {
+            return false;
+        }
+        
+        // size check
+        final Body body = attachment.getBody();
+        try {
+            if (
+                    body instanceof BinaryBody 
+                    && IOUtils.toByteArray(((BinaryBody) body).getInputStream()).length > maxSize
+                    || 
+                    body instanceof TextBody
+                    && IOUtils.toByteArray(((TextBody) body).getInputStream()).length > maxSize ) {
+                return false;
+            }
+        } catch (IOException e) {
+            return false;
+        }
+
+        // true, if nothing wrong
+        return true;
+    }
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ConnectorScheduler.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ConnectorScheduler.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ConnectorScheduler.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ConnectorScheduler.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,79 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.sling.mailarchiveserver.api.Connector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+public class ConnectorScheduler implements Runnable {
+
+	private static final int SLEEP_TIME_BETWEEN_NEW_MAIL_CHECKS = 20;
+
+	private static final Logger logger = LoggerFactory.getLogger(ConnectorScheduler.class);
+	private static final int WAITING_TIME_LIMIT_BEFORE_TERMINATION = 5000; 
+
+	private boolean running = true;
+	private Thread executionThread = null;
+
+	@Reference(
+			cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, 
+			policy=ReferencePolicy.DYNAMIC,
+			referenceInterface=Connector.class)
+	private List<Connector> scheduledConnectors = new ArrayList<Connector>();
+	private Deque<Connector> executionQueue;
+
+	@Activate
+	public void activate() {
+		executionThread = new Thread(this, "Connector Scheduler's execution thread");
+		executionThread.setDaemon(true);
+		executionThread.start(); 
+	}
+
+	@Deactivate
+	public void deactivate() throws InterruptedException {
+		if (executionThread != null) {
+			running = false;
+			executionThread.join(WAITING_TIME_LIMIT_BEFORE_TERMINATION);
+		}
+	}
+
+	@Override
+	public void run() {
+		while (running) {
+			executionQueue = new ArrayDeque<Connector>(scheduledConnectors);
+			while (!executionQueue.isEmpty() && running) {
+				Connector c = executionQueue.remove();
+				int retreived = c.checkNewMessages();
+				logger.info("Processed {} messages using \"{}\" connector.", retreived, c.toString());
+			}
+			try {
+				TimeUnit.SECONDS.sleep(SLEEP_TIME_BETWEEN_NEW_MAIL_CHECKS);
+			} catch (InterruptedException e) {
+				running = false;
+			}
+		}
+	}
+
+	public synchronized void bindConnector(Connector c) {
+		logger.info("Connector " + c.toString() + " added to pool.");
+		scheduledConnectors.add(c);
+	}
+
+	public synchronized void unbindConnector(Connector c) {
+		logger.info("Connector " + c.toString() + " removed from pool.");
+		scheduledConnectors.remove(c);
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ImportMboxServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ImportMboxServlet.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ImportMboxServlet.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ImportMboxServlet.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,51 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletException;
+
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.sling.SlingServlet;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.api.servlets.SlingAllMethodsServlet;
+import org.apache.sling.mailarchiveserver.api.MboxParser;
+import org.apache.sling.mailarchiveserver.api.MessageStore;
+import org.apache.sling.mailarchiveserver.util.MailArchiveServerConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("serial")
+@SlingServlet(
+		resourceTypes = "mailarchiveserver/import",
+		methods = {"POST", "PUT"})
+public class ImportMboxServlet extends SlingAllMethodsServlet {
+
+	private static final Logger logger = LoggerFactory.getLogger(ImportMboxServlet.class);
+
+	private static final String IMPORT_FILE_ATTRIB_NAME = "mboxfile";
+
+	@Reference
+	private MboxParser parser;
+	@Reference
+	private MessageStore store;
+
+	@Override
+	protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) 
+			throws ServletException, IOException {
+		RequestParameter param = request.getRequestParameter(IMPORT_FILE_ATTRIB_NAME);
+		if (param != null) {
+			logger.info("Processing attachment: " + param.toString());
+
+			InputStream mboxIS = param.getInputStream();
+			store.saveAll(parser.parse(mboxIS));
+
+			response.sendRedirect(MailArchiveServerConstants.ARCHIVE_PATH + ".html");
+		} else {
+			logger.info("No attachment to process.");
+		}
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/MessageStoreImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,440 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.james.mime4j.dom.field.FieldName.SUBJECT;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.CONTENT;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.HTML_BODY;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.LIST_ID;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.NAME;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.PLAIN_BODY;
+import static org.apache.sling.mailarchiveserver.util.MessageFieldName.X_ORIGINAL_HEADER;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.util.Text;
+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.Header;
+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.dom.field.FieldName;
+import org.apache.james.mime4j.message.BodyPart;
+import org.apache.james.mime4j.message.DefaultMessageWriter;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.sling.api.resource.LoginException;
+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.ResourceResolverFactory;
+import org.apache.sling.mailarchiveserver.api.AttachmentFilter;
+import org.apache.sling.mailarchiveserver.api.MessageProcessor;
+import org.apache.sling.mailarchiveserver.api.MessageStore;
+import org.apache.sling.mailarchiveserver.api.ThreadKeyGenerator;
+import org.apache.sling.mailarchiveserver.util.MailArchiveServerConstants;
+import org.apache.sling.mailarchiveserver.util.MessageFieldName;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service(MessageStore.class)
+public class MessageStoreImpl implements MessageStore {
+
+    public static final String PLAIN_MIMETYPE = "text/plain";
+    public static final String HTML_MIMETYPE = "text/html";
+
+    @Reference
+    private	ResourceResolverFactory resourceResolverFactory;
+    @Reference
+    ThreadKeyGenerator threadKeyGen;
+    @Reference
+    AttachmentFilter attachmentFilter;
+    @Reference(
+            cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, 
+            policy=ReferencePolicy.DYNAMIC,
+            referenceInterface=MessageProcessor.class)
+    private SortedSet<ServiceReference> messageProcessorRefs = new TreeSet<ServiceReference>();
+    private List<MessageProcessor> messageProcessors = new ArrayList<MessageProcessor>();
+    private boolean processorsUpdated;
+    private BundleContext bundleContext = null;
+
+    static final String FIELD_SEPARATOR = " : ";
+    private static final String[] RE_PREFIXES = { "re:", "aw:", "fw:", "re ", "aw ", "fw ", "答复"};
+    // for testing
+    String archivePath = MailArchiveServerConstants.ARCHIVE_PATH;
+    String resourceTypeKey = MailArchiveServerConstants.RT_KEY;
+
+    private static final Logger logger = LoggerFactory.getLogger(MessageStoreImpl.class);
+
+    @Activate
+    private void activate(BundleContext bc) {
+        bundleContext = bc;
+    }
+
+    protected ResourceResolver getResourceResolver() throws LoginException {
+        return resourceResolverFactory.getAdministrativeResourceResolver(null);
+    }
+
+    public void save(Message msg) throws IOException {
+        ResourceResolver resolver = null;
+        try {
+            resolver = getResourceResolver();
+            save(resolver, msg);
+        } catch (LoginException e) {
+            throw new RuntimeException("LoginException", e);
+        } finally {
+            if(resolver != null) {
+                resolver.close();
+            }
+        }
+    }
+
+    private void save(ResourceResolver resolver, Message msg) throws IOException, LoginException {
+        // apply message processors
+        for(MessageProcessor processor : getSortedMessageProcessors()) {
+            logger.debug("Calling {}", processor);
+            processor.processMessage(msg);
+        }
+
+        // into path: archive/domain/list/thread/message
+        final Map<String, Object> msgProps = new HashMap<String, Object>();
+        final List<BodyPart> attachments = new ArrayList<BodyPart>(); 
+
+        msgProps.put(resourceTypeKey, MailArchiveServerConstants.MESSAGE_RT);
+
+        StringBuilder plainBody = new StringBuilder();
+        StringBuilder htmlBody = new StringBuilder();
+        Boolean hasBody = false;
+
+        if (!msg.isMultipart()) {
+            plainBody = new StringBuilder(getTextPart(msg)); 
+        } else {
+            Multipart multipart = (Multipart) msg.getBody();
+            recursiveMultipartProcessing(multipart, plainBody, htmlBody, hasBody, attachments);
+        }
+
+        msgProps.put(PLAIN_BODY, plainBody.toString().replaceAll("\r\n", "\n"));
+        if (htmlBody.length() > 0) {
+            msgProps.put(HTML_BODY, htmlBody.toString());
+        }
+
+        msgProps.putAll(getMessagePropertiesFromHeader(msg.getHeader()));
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        new DefaultMessageWriter().writeHeader(msg.getHeader(), baos);
+        String origHdr = baos.toString(MailArchiveServerConstants.DEFAULT_ENCODER.charset().name());
+        msgProps.put(X_ORIGINAL_HEADER, origHdr);
+        
+        final Header hdr = msg.getHeader();
+        final String listIdRaw = hdr.getField(LIST_ID).getBody();
+        final String listId = listIdRaw.substring(1, listIdRaw.length()-1); // remove < and >
+
+        final String list = getListNodeName(listId);
+        final String domain = getDomainNodeName(listId);
+        final String subject = (String) msgProps.get(SUBJECT);
+        final String threadPath = threadKeyGen.getThreadKey(subject);
+        final String threadName = removeRe(subject);
+
+        Resource parentResource = assertEachNode(resolver, archivePath, domain, list, threadPath, threadName);
+
+        String msgNodeName = makeJcrFriendly((String) msgProps.get(NAME));
+        boolean isMsgNodeCreated = assertResource(resolver, parentResource, msgNodeName, msgProps);
+        if (isMsgNodeCreated) {
+            Resource msgResource = resolver.getResource(parentResource, msgNodeName);
+            for (BodyPart att : attachments) {
+                if (!attachmentFilter.isEligible(att)) {
+                    continue;
+                }
+                final Map<String, Object> attProps = new HashMap<String, Object>();
+                parseHeaderToProps(att.getHeader(), attProps);
+                Body body = att.getBody();
+                if (body instanceof BinaryBody) {
+                    attProps.put(CONTENT, ((BinaryBody) body).getInputStream());
+                } else if (body instanceof TextBody) {
+                    attProps.put(CONTENT, ((TextBody) body).getInputStream());
+                }
+
+                String attNodeName = Text.escapeIllegalJcrChars(att.getFilename());
+                assertResource(resolver, msgResource, attNodeName, attProps);
+            }
+
+            updateThread(resolver, parentResource, msgProps);
+        }
+    }
+
+    static void recursiveMultipartProcessing(Multipart multipart, StringBuilder plainBody, StringBuilder htmlBody, Boolean hasBody, List<BodyPart> attachments) throws IOException {
+        for (Entity enitiy : multipart.getBodyParts()) {
+            BodyPart part = (BodyPart) enitiy;
+            if (part.getDispositionType() != null && !part.getDispositionType().equals("")) {
+                // if DispositionType is null or empty, it means that it's multipart, not attached file
+                attachments.add(part);
+            } else {
+                if (part.isMimeType(PLAIN_MIMETYPE) && !hasBody) {
+                    plainBody.append(getTextPart(part));
+                    hasBody = true;
+                } else if (part.isMimeType(HTML_MIMETYPE) && !hasBody) {
+                    htmlBody.append(getTextPart(part));
+                } else if (part.isMultipart()) {
+                    recursiveMultipartProcessing((Multipart) part.getBody(), plainBody, htmlBody, hasBody, attachments);
+                } 
+            }
+        }
+    }
+
+    public void saveAll(Iterator<Message> iterator) throws IOException {
+        ResourceResolver resolver = null;
+        try {
+            resolver = getResourceResolver();
+            int mcount = 0;
+            while (iterator.hasNext()) {
+                Message msg = iterator.next();
+                save(resolver, msg);
+
+                mcount++;
+                if (mcount % 100 == 0) {
+                    logger.debug(mcount+" messages processed.");
+                }
+            }
+            logger.info(mcount+" messages processed.");
+        } catch(LoginException e) {
+            throw new RuntimeException("LoginException", e);
+        } finally {
+            if(resolver != null) {
+                resolver.close();
+            }
+        }
+    }
+
+    /**
+     *	code taken from http://www.mozgoweb.com/posts/how-to-parse-mime-message-using-mime4j-library/
+     */
+    static String getTextPart(Entity part) throws IOException {
+        TextBody tb = (TextBody) part.getBody();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        tb.writeTo(baos);
+        return baos.toString(MailArchiveServerConstants.DEFAULT_ENCODER.charset().name());
+    }
+
+    static Map<String, Object> getMessagePropertiesFromHeader(Header hdr) {
+        Map<String, Object> props = new HashMap<String, Object>();
+
+        parseHeaderToProps(hdr, props);
+
+        // message name
+        String name;
+        if (hdr.getField("Message-ID") != null) {
+            name = hdr.getField("Message-ID").getBody();
+            name = name.substring(1, name.length()-1); // remove < and >
+        } else {
+            name = Integer.toHexString(hdr.getField("Date").hashCode());
+        }
+        props.put(MessageFieldName.NAME, name);
+
+        return props;
+    }
+
+    private static void parseHeaderToProps(Header hdr, Map<String, Object> props) {
+        Set<String> processed = new HashSet<String>();
+        for (Field f : hdr.getFields()) {
+            String name = f.getName();
+            if (!processed.contains(name)) {
+                processed.add(name);
+                String value = "";
+                List<Field> fields = hdr.getFields(name);
+                for (Field fl : fields) {
+                    value += fl.getBody()+FIELD_SEPARATOR;
+                }
+                props.put(name, value.substring(0, value.length()-FIELD_SEPARATOR.length()));
+            }
+        }
+    }
+
+    static String getListNodeName(String listId) {
+        String list = "";
+        String[] split = listId.split("\\.");
+        int splitL = split.length;
+        if (splitL >= 4) {
+            for (int i = 0; i < splitL-2; i++) {
+                list += split[i] + ".";
+            }
+            list = list.substring(0, list.length() - 1);
+        } else if (splitL == 3) {
+            list = split[0];
+        } else {
+            throw new RuntimeException("List-Id is invalid: minimum 2 separatory dots required.");
+        }
+        return list;
+    }
+
+    static String getDomainNodeName(String listId) {
+        String[] split = listId.split("\\.");
+        int splitL = split.length;
+        if (splitL >= 3) {
+            return split[splitL-2] + "." + split[splitL-1];
+        } else {
+            throw new RuntimeException("List-Id is invalid: minimum 2 separatory dots required.");
+        }
+    }
+
+    private Resource assertEachNode(ResourceResolver resolver, String archive, String domain, String list, 
+            String threadPath, String threadName) throws PersistenceException, LoginException {
+        final String pathToMessage = archive+domain+"/"+list+"/"+threadPath;
+
+        String path = pathToMessage;
+        Resource resource = resolver.getResource(path);
+
+        int cnt = 0;
+        while (resource == null) {
+            cnt++;
+            path = path.substring(0, path.lastIndexOf("/"));
+            resource = resolver.getResource(path);
+        }
+
+        if (cnt > 0) {
+            int threadNodesNumber = threadPath.split("/").length;
+
+            // bind paths
+            List<String> nodePaths = new ArrayList<String>();
+            nodePaths.add(domain);
+            nodePaths.add(list);
+            for (String node : threadPath.split("/")) {
+                nodePaths.add(node);
+            }
+
+            // bind props
+            List<Map<String, Object>> nodeProps = new ArrayList<Map<String, Object>>();
+            nodeProps.add(setProperties(MailArchiveServerConstants.DOMAIN_RT, domain));
+            nodeProps.add(setProperties(MailArchiveServerConstants.LIST_RT, list));
+            for (int i = 0; i < threadNodesNumber-1; i++) {
+                nodeProps.add(null);
+            }
+            nodeProps.add(setProperties(MailArchiveServerConstants.THREAD_RT, threadName));
+
+            // checking
+            for (int i = nodePaths.size()-cnt; i < nodePaths.size(); i++) {
+                String name = nodePaths.get(i);
+                assertResource(resolver, resource, name, nodeProps.get(i));
+                resource = resolver.getResource(resource.getPath()+"/"+name);
+            }
+        }
+
+        resource = resolver.getResource(pathToMessage);
+        if (resource == null) {
+            throw new RuntimeException("Parent resource cannot be null.");
+        } else {
+            return resource;
+        }
+
+    }
+
+    private Map<String, Object> setProperties(String resourceType, String name) {
+        Map<String, Object> props = new HashMap<String, Object>();
+        props.put(resourceTypeKey, resourceType);
+        props.put(MessageFieldName.NAME, name);
+        return props;
+
+    }
+
+    private boolean assertResource(ResourceResolver resolver, Resource parent, String name, Map<String, Object> newProps) 
+            throws LoginException, PersistenceException {
+        String checkPath = parent.getPath()+"/"+name;
+        final Resource checkResource = resolver.getResource(checkPath);
+        if (checkResource == null) {
+            final Resource newResource = resolver.create(parent, name, newProps);
+            resolver.commit();
+            logger.debug(String.format("Resource created at %s .", newResource.getPath()));
+            return true;
+        } else {
+            logger.debug(String.format("Resource at %s already exists.", checkResource.getPath()));
+            return false;
+        }
+    }
+
+    private static void updateThread(ResourceResolver resolver, Resource thread, Map<String, Object> msgProps) throws PersistenceException {
+        final ModifiableValueMap thrdProps = thread.adaptTo(ModifiableValueMap.class);
+        Long prop = (Long) thrdProps.get(MessageFieldName.LAST_UPDATE);
+        Date updatedDate = null; 
+        if (prop != null) {
+            updatedDate = new Date(prop);
+        }
+        final String msgProp = (String) msgProps.get(FieldName.DATE);
+        SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
+        final Date msgDate = sdf.parse(msgProp, new ParsePosition(0));
+        if (updatedDate == null || msgDate.after(updatedDate)) {
+            thrdProps.put(MessageFieldName.LAST_UPDATE, msgDate.getTime());
+            resolver.commit();
+        } 
+    }
+
+    static String makeJcrFriendly(String s) {
+        return s.replaceAll("[\\s\\.-]+", "_").replaceAll("\\W", "").replaceAll("\\_", " ").trim().replaceAll("[ ]+", "_");
+    }
+
+    static String removeRe(String s) {
+        s = s.trim();
+        boolean flag = true;
+        while (flag) {
+            flag = false;
+            for (String prefix : RE_PREFIXES) {
+                if (s.toLowerCase().startsWith(prefix)) {
+                    s = s.substring(3).trim();
+                    flag = true;
+                }
+            }
+        }
+        return s.trim();
+    }
+
+    public synchronized void bindMessageProcessor(ServiceReference ref) {
+        synchronized (messageProcessorRefs) {
+            messageProcessorRefs.add(ref);
+        }
+        processorsUpdated = true;
+        logger.info("Message processor {} added to pool.", ref);
+    }
+
+    public void unbindMessageProcessor(ServiceReference ref) {
+        synchronized (messageProcessorRefs) {
+            messageProcessorRefs.remove(ref);
+        }
+        processorsUpdated = true;
+        logger.info("Message processor {} removed from pool.", ref);
+    }
+
+    private Collection<MessageProcessor> getSortedMessageProcessors() {
+        if(processorsUpdated) {
+            synchronized (messageProcessorRefs) {
+                processorsUpdated = false;
+                messageProcessors.clear();
+                for(ServiceReference ref : messageProcessorRefs) {
+                    messageProcessors.add((MessageProcessor)bundleContext.getService(ref));
+                }
+            }
+            logger.debug("Updated sorted list of MessageProcessor: {}", messageProcessors);
+        }
+        return messageProcessors;
+    }
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/Mime4jMboxParserImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,100 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Iterator;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+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.mboxiterator.CharBufferWrapper;
+import org.apache.james.mime4j.mboxiterator.MboxIterator;
+import org.apache.james.mime4j.message.DefaultMessageBuilder;
+import org.apache.sling.mailarchiveserver.api.MboxParser;
+import org.apache.sling.mailarchiveserver.util.MailArchiveServerConstants;
+
+@Component
+@Service(MboxParser.class)
+public class Mime4jMboxParserImpl implements MboxParser {
+
+	@Override
+	public Iterator<Message> parse(InputStream is) throws IOException {
+		return new Mime4jParserIterator(is);
+	}
+
+	static class Mime4jParserIterator implements Iterator<Message> {
+
+		private Iterator<CharBufferWrapper> mboxIterator;
+		private static final int BUFFER_SIZE = 10*1024*1024;
+		String tempFileAbsPath = null;
+
+		public Mime4jParserIterator(InputStream is) throws IOException {
+			File tempFile = null;
+			FileOutputStream fileOS = null;
+			try {
+				// create temp file
+				tempFile = File.createTempFile("MAS_", ".mbox");
+				tempFileAbsPath = tempFile.getAbsolutePath();
+				fileOS = new FileOutputStream(tempFile);
+				FileChannel fileChannel = fileOS.getChannel();
+				byte[] buffer = new byte[BUFFER_SIZE];
+				int read = 0;
+				while ((read = is.read(buffer)) != -1) {
+					ByteBuffer buf2 = MailArchiveServerConstants.DEFAULT_ENCODER.encode(CharBuffer.wrap(new String(buffer, 0, read)));
+					fileChannel.write(buf2);
+				}
+				fileChannel.close();
+
+
+				createMboxIterator(tempFile);
+			} finally {
+				if (tempFile.exists()) {
+					tempFile.delete();
+					tempFile = null;
+				}
+				if (fileOS != null) {
+					fileOS.close();
+					fileOS = null;
+				}
+				if (is != null) {
+					is.close();
+					is = null;
+				}
+			}
+		}
+
+		private void createMboxIterator(File f) throws FileNotFoundException, IOException {
+			mboxIterator = MboxIterator.fromFile(f).charset(MailArchiveServerConstants.DEFAULT_ENCODER.charset()).build().iterator();
+		}
+
+		public boolean hasNext() {
+			return mboxIterator.hasNext();
+		}
+
+		public Message next() {
+			MessageBuilder builder = new DefaultMessageBuilder();
+			Message message = null;
+			try {
+				message = builder.parseMessage(new ByteArrayInputStream(mboxIterator.next().toString().getBytes(MailArchiveServerConstants.DEFAULT_ENCODER.charset().name())));
+			} catch (MimeException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			return message;
+		}
+
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+	}
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/QueryBuilderImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/QueryBuilderImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/QueryBuilderImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/QueryBuilderImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,96 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static org.apache.sling.mailarchiveserver.impl.SearchQueryParserImpl.MESSAGE_FIELDS;
+import static org.apache.sling.mailarchiveserver.impl.SearchQueryParserImpl.SEARCH_PARAMETER_TO_MESSAGE_FIELD_MAP;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.mailarchiveserver.api.QueryBuilder;
+import org.apache.sling.mailarchiveserver.impl.SearchQueryParserImpl.SearchParameter;
+
+@Component
+@Service(QueryBuilder.class)
+public class QueryBuilderImpl implements QueryBuilder {
+
+	static final String BASE = "SELECT * FROM [nt:unstructured] WHERE [sling:resourceType] = 'mailarchiveserver/message'";
+	static final String DUMMY = "SELECT * FROM [nt:frozenNode] WHERE [42] = 0";
+	private static final String AND = " AND ";
+	private static final String OR = " OR ";
+
+	public static final String SQL2 = "SQL2";
+
+	private String buildSQL2Query(Map<String, List<String>> tokens) {
+		if (tokens == null) {
+			return BASE; 
+		} else if (tokens.size() == 0) {
+			return DUMMY;
+		}
+
+		String constraints = "";
+
+		// tokens constraints
+		for (String tokenClass : tokens.keySet()) {
+			if (tokenClass.equals(SearchParameter.NONE)) {
+				continue;
+			}
+			String fieldConstraint =  buildFieldConstraints(tokenClass, tokens);
+			constraints += "("+ fieldConstraint +")" + AND;
+		}
+
+		// global constraints
+		String globalConstraint = "";
+		List<String> ls = tokens.get(SearchParameter.NONE);
+		if (ls != null) {
+			for (String msgField : MESSAGE_FIELDS) {
+				for (String value : ls) {
+					if (!value.trim().equals("")) {
+						globalConstraint += sqlLikeConstraint(sqlLower(msgField), value.toLowerCase()) + OR;
+					}
+				}
+			}
+			globalConstraint = globalConstraint.substring(0, globalConstraint.length()-OR.length());
+		}
+		if (!globalConstraint.equals("")) {
+			constraints += "("+ globalConstraint +")" + AND;
+		}
+
+		if (constraints.equals("")) {
+			return BASE;
+		} else {
+			return BASE + " AND " + constraints.substring(0, constraints.length()-AND.length());
+		}
+	}
+
+	private static String buildFieldConstraints(String tokenClass, Map<String, List<String>> tokens) {
+		List<String> ls = tokens.get(tokenClass);
+		String messageField = SEARCH_PARAMETER_TO_MESSAGE_FIELD_MAP.get(tokenClass);
+		String fieldConstraint = "";
+		for (String value : ls) {
+			if (!value.trim().equals("")) {
+				fieldConstraint += sqlLikeConstraint(sqlLower(messageField), value.toLowerCase()) + OR;
+			}
+		}
+		return fieldConstraint.substring(0, fieldConstraint.length()-OR.length());
+	}
+
+	private static String sqlLikeConstraint(String messageField, String value) {
+		return messageField + " LIKE '%" + value + "%'";
+	}
+
+	private static String sqlLower(String messageField) {
+		return "LOWER("+ messageField + ")";
+	}
+
+	@Override
+	public String buildQuery(Map<String, List<String>> tokens, String lang) {
+		if (lang.trim().equalsIgnoreCase(SQL2)) {
+			return buildSQL2Query(tokens);
+		} else {
+			throw new IllegalArgumentException("Invalid lang argument!");
+		}
+	}
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchQueryParserImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchQueryParserImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchQueryParserImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchQueryParserImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,118 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.mailarchiveserver.api.SearchQueryParser;
+
+@Component
+@Service(SearchQueryParser.class)
+public class SearchQueryParserImpl implements SearchQueryParser {
+
+
+	private static final String SPACE_SUBSTITUTION = ".-.";
+
+	@Override
+	public Map<String, List<String>> parse(String phrase) {
+		Map<String, List<String>> res = new HashMap<String, List<String>>();
+		phrase = phrase.trim();
+		if (phrase == "") {
+			return null;
+		}
+
+		phrase = parseQuotes(phrase);
+
+		String[] lexemes = phrase.split(" ");
+		for (String lexeme : lexemes) {
+			String[] token = lexeme.split(":");
+			if (token.length == 1) {
+				insertTokenIntoMap(postprocessQuotedPhrase(token[0].trim()), SearchParameter.NONE, res);
+			} else if (token.length > 1) {
+				String searchParam = getSearchParameter(token[0]);
+				if (searchParam != null) {
+					insertTokenIntoMap(postprocessQuotedPhrase(token[1].trim()), searchParam, res);
+				}
+			}
+		}
+		return res;
+	}
+
+	private String postprocessQuotedPhrase(String phrase) {
+		return phrase.replace(SPACE_SUBSTITUTION, " ");
+	}
+
+	private String parseQuotes(String phrase) {
+		String[] quotes = phrase.split("\"");
+		for (int i = 1; i < quotes.length; i += 2) {
+			quotes[i] = quotes[i].replace(" ", SPACE_SUBSTITUTION);
+		}
+		String res = "";
+		for (String s : quotes) {
+			res += s;
+		}
+		return res;
+	}
+
+	private static String getSearchParameter(String s) {
+		s = s.trim().toLowerCase();
+		if (SEARCH_PARAMETERES.contains(s)) {
+			return s;
+		} else {
+			return null;
+		}
+	}
+
+	private static void insertTokenIntoMap(String tokenString, String tokenClass, Map<String, List<String>> map) {
+		List<String> ls = map.get(tokenClass);
+		if (ls == null) {
+			ls = new ArrayList<String>();
+		}
+		ls.add(tokenString);
+		map.put(tokenClass, ls);
+	}
+
+	public static final Set<String> SEARCH_PARAMETERES = new HashSet<String>();
+	public static final Set<String> MESSAGE_FIELDS = new HashSet<String>();
+	public static final Map<String, String> SEARCH_PARAMETER_TO_MESSAGE_FIELD_MAP = new HashMap<String, String>();
+
+	static {
+		SEARCH_PARAMETERES.add(SearchParameter.FROM);
+		SEARCH_PARAMETERES.add(SearchParameter.SUBJ);
+		SEARCH_PARAMETERES.add(SearchParameter.LIST);
+
+		MESSAGE_FIELDS.add(SearchableMessageField.FROM);
+		MESSAGE_FIELDS.add(SearchableMessageField.SUBJ);
+		MESSAGE_FIELDS.add(SearchableMessageField.LIST);
+		MESSAGE_FIELDS.add(SearchableMessageField.BODY);
+
+		SEARCH_PARAMETER_TO_MESSAGE_FIELD_MAP.put(SearchParameter.FROM, SearchableMessageField.FROM);
+		SEARCH_PARAMETER_TO_MESSAGE_FIELD_MAP.put(SearchParameter.SUBJ, SearchableMessageField.SUBJ);
+		SEARCH_PARAMETER_TO_MESSAGE_FIELD_MAP.put(SearchParameter.LIST, SearchableMessageField.LIST);
+	}
+
+	public static class SearchParameter {
+		public static final String NONE = ""; // not in SEARCH_PARAMETERES !
+		public static final String FROM = "from";
+		public static final String SUBJ = "subject";
+		public static final String LIST = "list";
+	}
+
+	public static class SearchableMessageField {
+		public static final String FROM = "From";
+		public static final String SUBJ = "Subject";
+		public static final String LIST = "'List-Id'";
+		public static final String BODY = "Body";
+		//		public static final String DATE = "";
+		//		public static final String FROM = "";
+		//		public static final String TO = "";
+
+
+	}
+
+}
\ No newline at end of file

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchServiceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchServiceImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchServiceImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/SearchServiceImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,42 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import java.util.Iterator;
+
+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.felix.scr.annotations.Service;
+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.mailarchiveserver.api.SearchQueryParser;
+import org.apache.sling.mailarchiveserver.api.SearchService;
+
+@Component
+@Service(SearchService.class)
+public class SearchServiceImpl implements SearchService {
+
+	@Reference
+	private	ResourceResolverFactory resourceResolverFactory;
+	ResourceResolver resolver = null;
+	@Reference 
+	private SearchQueryParser parser;
+	private QueryBuilderImpl queryBuilder = new QueryBuilderImpl();
+
+	@Activate
+	public void activate() throws LoginException {
+		if (resolver == null) {
+			resolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
+		}
+	}
+
+	@Override
+	public Iterator<Resource> find(String phrase) {
+		String query = queryBuilder.buildQuery(parser.parse(phrase), QueryBuilderImpl.SQL2);
+		Iterator<Resource> res = resolver.findResources(query, Query.JCR_SQL2);
+		return res;
+	}
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImpl.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImpl.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/impl/ThreadKeyGeneratorImpl.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,92 @@
+package org.apache.sling.mailarchiveserver.impl;
+
+import static java.lang.Character.isLetterOrDigit;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.makeJcrFriendly;
+import static org.apache.sling.mailarchiveserver.impl.MessageStoreImpl.removeRe;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.sling.mailarchiveserver.api.ThreadKeyGenerator;
+
+@Component
+@Service(ThreadKeyGenerator.class)
+public class ThreadKeyGeneratorImpl implements ThreadKeyGenerator {
+
+    /**
+     * These constants are gotten by sampling around 1500 messages 
+     * from some open source projects using SubjectLettersEntropy class.
+     * 
+     * Entropy of each letter position of the subject was calculated. And two letter positions 
+     * with biggest entropy and occurrence in at least 90% of the messages were chosen.
+     * 
+     * In order to {@link #getThreadKey(Message)} works correctly, following invariant should hold: LETTER_POS_WITH_BIGGEST_ENTROPY < LETTER_POS_WITH_2ND_BIGGEST_ENTROPY
+     */
+    private static final int LETTER_POS_WITH_BIGGEST_ENTROPY = 9;
+    private static final int LETTER_POS_WITH_2ND_BIGGEST_ENTROPY = 40;
+    private static final String UNADDRESSABLE_SUBJECT = "unaddressable subject";
+
+    public String getThreadKey(String subject) {
+        String wordCharsSubj;
+        String noReSubj;
+        if (subject != null) {
+            noReSubj = removeRe(subject);
+            wordCharsSubj = noReSubj.replaceAll("\\W", "_");
+            if (!isAddressable(wordCharsSubj)) {
+                noReSubj = wordCharsSubj = UNADDRESSABLE_SUBJECT;
+            }
+        } else {
+            noReSubj = wordCharsSubj = UNADDRESSABLE_SUBJECT;
+        }
+
+        char prefix1;
+        char prefix2;
+        prefix1 = assignPrefix(wordCharsSubj, LETTER_POS_WITH_BIGGEST_ENTROPY);
+        prefix2 = assignPrefix(wordCharsSubj, LETTER_POS_WITH_2ND_BIGGEST_ENTROPY);
+        return ""+prefix1+"/"+prefix1+prefix2+"/"+ makeJcrFriendly(noReSubj);
+    }
+
+    private static boolean isAddressable(String subject) {
+        for (char c : subject.toCharArray()) {
+            if (isLetterOrDigit(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static char assignPrefix(String subject, int length) {
+        char prefix;
+        if (subject.length() > length) {
+
+            int i = length;
+            while (i > -1 && !isLetterOrDigit(subject.charAt(i))) 
+                i--;
+            if (i > -1) 
+                prefix = subject.charAt(i);
+            else {
+                i = length;
+                while (i<subject.length() && !isLetterOrDigit(subject.charAt(i))) 
+                    i++;
+                if (i<subject.length()) 
+                    prefix = subject.charAt(i);
+                else 
+                    throw new IllegalArgumentException();
+            }
+
+        } else {
+
+            int i = subject.length()-1;
+            while (i > -1 && !isLetterOrDigit(subject.charAt(i))) 
+                i--;
+            if (i > -1) 
+                prefix = subject.charAt(i);
+            else 
+                throw new IllegalArgumentException();
+
+        }
+
+        return prefix;
+    }
+
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MailArchiveServerConstants.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MailArchiveServerConstants.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MailArchiveServerConstants.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MailArchiveServerConstants.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,18 @@
+package org.apache.sling.mailarchiveserver.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+public class MailArchiveServerConstants {
+	
+	public static final String ARCHIVE_PATH = "/content/mailarchiveserver/archive/";
+	
+	//RT = ResourceType
+	public static final String RT_KEY = "sling:resourceType";
+	public static final String DOMAIN_RT = "mailarchiveserver/domain";
+	public static final String LIST_RT = "mailarchiveserver/list";
+	public static final String THREAD_RT = "mailarchiveserver/thread";
+	public static final String MESSAGE_RT = "mailarchiveserver/message";
+
+	public static final CharsetEncoder DEFAULT_ENCODER = Charset.forName("UTF-8").newEncoder();
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MessageFieldName.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MessageFieldName.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MessageFieldName.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/MessageFieldName.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,20 @@
+package org.apache.sling.mailarchiveserver.util;
+
+
+public class MessageFieldName {
+	public static final String LIST_ID = "List-Id"; 
+	public static final String IN_REPLY_TO = "In-Reply-To"; 
+	public static final String LINK_ID = "linkId";
+    public static final String NAME = "jcr:text";
+    public static final String CONTENT = "jcr:data";
+    public static final String PLAIN_BODY = "Body";
+    public static final String HTML_BODY = "htmlBody";
+    public static final String LAST_UPDATE = "lastUpdate";
+    public static final String X_IMPORT_LOG = "X-mailarchive-import";
+    public static final String X_ADDC_PATH = "X-addc-path";
+    public static final String X_ORIGINAL_HEADER = "X-original-header";
+    public static final String ENCODING = "jcr:encoding";
+    
+//    public static final String X_ORIGINAL_MESSAGE = "X-original-message";
+//    public static final String FILENAME = "filename";
+}

Added: sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/SysStreamsLogger.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/SysStreamsLogger.java?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/SysStreamsLogger.java (added)
+++ sling/trunk/samples/mail-archive/server/src/main/java/org/apache/sling/mailarchiveserver/util/SysStreamsLogger.java Mon Dec 23 11:11:15 2013
@@ -0,0 +1,194 @@
+package org.apache.sling.mailarchiveserver.util;
+
+/**
+ * Util class to log System.err and System.out streams.
+ * 
+ * taken from http://stackoverflow.com/questions/11187461/redirect-system-out-and-system-err-to-slf4j
+ * @author itshorty
+ */
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SysStreamsLogger {
+	private static Logger sysOutLogger = LoggerFactory.getLogger("SYSOUT");
+	private static Logger sysErrLogger = LoggerFactory.getLogger("SYSERR");
+
+	public static final PrintStream sysout = System.out;
+	public static final PrintStream syserr = System.err;
+
+	protected static final String LINE_SEPERATOR = System.getProperty("line.separator");
+
+	public static void bindSystemStreams() {
+		// Enable autoflush
+		System.setOut(new PrintStream(new LoggingOutputStream(sysOutLogger, false), true));
+		System.setErr(new PrintStream(new LoggingOutputStream(sysErrLogger, true), true));
+	}
+
+	public static void unbindSystemStreams() {
+		System.setOut(sysout);
+		System.setErr(syserr);
+	}
+
+	private static class LoggingOutputStream extends java.io.OutputStream {
+
+		protected Logger log;
+		protected boolean isError;
+
+		/**
+		 * Used to maintain the contract of {@link #close()}.
+		 */
+		protected boolean hasBeenClosed = false;
+
+		/**
+		 * The internal buffer where data is stored.
+		 */
+		protected byte[] buf;
+
+		/**
+		 * The number of valid bytes in the buffer. This value is always in the
+		 * range <tt>0</tt> through <tt>buf.length</tt>; elements
+		 * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid byte
+		 * data.
+		 */
+		protected int count;
+
+		/**
+		 * Remembers the size of the buffer for speed.
+		 */
+		private int bufLength;
+
+		/**
+		 * The default number of bytes in the buffer. =2048
+		 */
+		public static final int DEFAULT_BUFFER_LENGTH = 2048;
+
+		private LoggingOutputStream() {
+			// illegal
+		}
+
+		/**
+		 * Creates the LoggingOutputStream to flush to the given Category.
+		 * 
+		 * @param log
+		 *            the Logger to write to
+		 * 
+		 * @param isError
+		 *            the if true write to error, else info
+		 * 
+		 * @exception IllegalArgumentException
+		 *                if cat == null or priority == null
+		 */
+		public LoggingOutputStream(Logger log, boolean isError) throws IllegalArgumentException {
+			if (log == null) {
+				throw new IllegalArgumentException("log == null");
+			}
+
+			this.isError = isError;
+			this.log = log;
+			bufLength = DEFAULT_BUFFER_LENGTH;
+			buf = new byte[DEFAULT_BUFFER_LENGTH];
+			count = 0;
+		}
+
+		/**
+		 * Closes this output stream and releases any system resources
+		 * associated with this stream. The general contract of
+		 * <code>close</code> is that it closes the output stream. A closed
+		 * stream cannot perform output operations and cannot be reopened.
+		 */
+		@Override
+		public void close() {
+			flush();
+			hasBeenClosed = true;
+		}
+
+		/**
+		 * Writes the specified byte to this output stream. The general contract
+		 * for <code>write</code> is that one byte is written to the output
+		 * stream. The byte to be written is the eight low-order bits of the
+		 * argument <code>b</code>. The 24 high-order bits of <code>b</code> are
+		 * ignored.
+		 * 
+		 * @param b
+		 *            the <code>byte</code> to write
+		 */
+		@Override
+		public void write(final int b) throws IOException {
+			if (hasBeenClosed) {
+				throw new IOException("The stream has been closed.");
+			}
+
+			// don't log nulls
+			if (b == 0) {
+				return;
+			}
+
+			// would this be writing past the buffer?
+			if (count == bufLength) {
+				// grow the buffer
+				final int newBufLength = bufLength + DEFAULT_BUFFER_LENGTH;
+				final byte[] newBuf = new byte[newBufLength];
+
+				System.arraycopy(buf, 0, newBuf, 0, bufLength);
+
+				buf = newBuf;
+				bufLength = newBufLength;
+			}
+
+			buf[count] = (byte) b;
+			count++;
+		}
+
+		/**
+		 * Flushes this output stream and forces any buffered output bytes to be
+		 * written out. The general contract of <code>flush</code> is that
+		 * calling it is an indication that, if any bytes previously written
+		 * have been buffered by the implementation of the output stream, such
+		 * bytes should immediately be written to their intended destination.
+		 */
+		@Override
+		public void flush() {
+
+			if (count == 0) {
+				return;
+			}
+
+			// don't print out blank lines; flushing from PrintStream puts out
+			// these
+			if (count == LINE_SEPERATOR.length()) {
+				if (((char) buf[0]) == LINE_SEPERATOR.charAt(0) && ((count == 1) || // <-
+						// Unix
+						// &
+						// Mac,
+						// ->
+						// Windows
+						((count == 2) && ((char) buf[1]) == LINE_SEPERATOR.charAt(1)))) {
+					reset();
+					return;
+				}
+			}
+
+			final byte[] theBytes = new byte[count];
+
+			System.arraycopy(buf, 0, theBytes, 0, count);
+
+			if (isError) {
+				log.error(new String(theBytes));
+			} else {
+				log.info(new String(theBytes));
+			}
+
+			reset();
+		}
+
+		private void reset() {
+			// not resetting the buffer -- assuming that if it grew that it
+			// will likely grow similarly again
+			count = 0;
+		}
+	}
+}
\ No newline at end of file

Added: sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/content/mailarchiveserver.json
URL: http://svn.apache.org/viewvc/sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/content/mailarchiveserver.json?rev=1553094&view=auto
==============================================================================
--- sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/content/mailarchiveserver.json (added)
+++ sling/trunk/samples/mail-archive/server/src/main/resources/initial-content/content/mailarchiveserver.json Mon Dec 23 11:11:15 2013
@@ -0,0 +1,16 @@
+{
+    "sling:resourceType":"mailarchiveserver/root",
+    "jcr:primaryType": "nt:unstructured",
+
+    "archive" : {
+        "sling:resourceType": "mailarchiveserver/archive"
+    },
+
+    "import" : {
+        "sling:resourceType": "mailarchiveserver/import"
+    },
+
+    "search" : {
+        "sling:resourceType": "mailarchiveserver/search"
+    }
+}
\ No newline at end of file



Mime
View raw message