You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2017/11/15 11:15:43 UTC
[02/19] james-project git commit: JAMES-2214 Introduce helper class
for saving and sending emails with JMAP
JAMES-2214 Introduce helper class for saving and sending emails with JMAP
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/4dbe064d
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/4dbe064d
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/4dbe064d
Branch: refs/heads/master
Commit: 4dbe064d2e516804f2a058313f83fd7ce078f838
Parents: b539779
Author: benwa <bt...@linagora.com>
Authored: Mon Nov 13 13:22:44 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Wed Nov 15 17:58:15 2017 +0700
----------------------------------------------------------------------
.../james/jmap/methods/MessageAppender.java | 113 ++++++++++++++++++
.../james/jmap/methods/MessageSender.java | 70 +++++++++++
.../methods/SetMessagesCreationProcessor.java | 117 ++-----------------
.../SetMessagesCreationProcessorTest.java | 20 +++-
4 files changed, 211 insertions(+), 109 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/4dbe064d/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
new file mode 100644
index 0000000..4b1fad6
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
@@ -0,0 +1,113 @@
+/****************************************************************
+ * 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.james.jmap.methods;
+
+import java.util.Date;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.mail.util.SharedByteArrayInputStream;
+
+import org.apache.james.jmap.model.Attachment;
+import org.apache.james.jmap.model.Keywords;
+import org.apache.james.jmap.model.MessageFactory;
+import org.apache.james.mailbox.AttachmentManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.exception.AttachmentNotFoundException;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.Cid;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.util.OptionalUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+import com.github.fge.lambdas.functions.ThrowingFunction;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableList;
+
+public class MessageAppender {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MessageAppender.class);
+
+ private final AttachmentManager attachmentManager;
+ private final MIMEMessageConverter mimeMessageConverter;
+
+ @Inject
+ public MessageAppender(AttachmentManager attachmentManager, MIMEMessageConverter mimeMessageConverter) {
+ this.attachmentManager = attachmentManager;
+ this.mimeMessageConverter = mimeMessageConverter;
+ }
+
+ public MessageFactory.MetaDataWithContent createMessageInMailbox(ValueWithId.MessageWithId.CreationMessageEntry createdEntry,
+ MessageManager mailbox,
+ MailboxSession session) throws MailboxException {
+ ImmutableList<MessageAttachment> messageAttachments = getMessageAttachments(session, createdEntry.getValue().getAttachments());
+ byte[] messageContent = mimeMessageConverter.convert(createdEntry, messageAttachments);
+ SharedByteArrayInputStream content = new SharedByteArrayInputStream(messageContent);
+ Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
+
+ Keywords keywords = createdEntry.getValue()
+ .getKeywords()
+ .orElse(Keywords.DEFAULT_VALUE);
+ boolean notRecent = false;
+
+ ComposedMessageId message = mailbox.appendMessage(content, internalDate, session, notRecent, keywords.asFlags());
+
+ return MessageFactory.MetaDataWithContent.builder()
+ .uid(message.getUid())
+ .keywords(keywords)
+ .internalDate(internalDate.toInstant())
+ .sharedContent(content)
+ .size(messageContent.length)
+ .attachments(messageAttachments)
+ .mailboxId(mailbox.getId())
+ .messageId(message.getMessageId())
+ .build();
+ }
+
+ private ImmutableList<MessageAttachment> getMessageAttachments(MailboxSession session, ImmutableList<Attachment> attachments) throws MailboxException {
+ ThrowingFunction<Attachment, Optional<MessageAttachment>> toMessageAttachment = att -> messageAttachment(session, att);
+ return attachments.stream()
+ .map(Throwing.function(toMessageAttachment).sneakyThrow())
+ .flatMap(OptionalUtils::toStream)
+ .collect(Guavate.toImmutableList());
+ }
+
+ private Optional<MessageAttachment> messageAttachment(MailboxSession session, Attachment attachment) throws MailboxException {
+ try {
+ return Optional.of(MessageAttachment.builder()
+ .attachment(attachmentManager.getAttachment(AttachmentId.from(attachment.getBlobId().getRawValue()), session))
+ .name(attachment.getName().orElse(null))
+ .cid(attachment.getCid().map(Cid::from).orElse(null))
+ .isInline(attachment.isIsInline())
+ .build());
+ } catch (AttachmentNotFoundException e) {
+ // should not happen (checked before)
+ LOGGER.error(String.format("Attachment %s not found", attachment.getBlobId()), e);
+ return Optional.empty();
+ } catch (IllegalStateException e) {
+ LOGGER.error(String.format("Attachment %s is not well-formed", attachment.getBlobId()), e);
+ return Optional.empty();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/4dbe064d/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
new file mode 100644
index 0000000..dd94618
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * 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.james.jmap.methods;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.mail.MessagingException;
+
+import org.apache.james.jmap.model.CreationMessageId;
+import org.apache.james.jmap.model.Message;
+import org.apache.james.jmap.model.MessageFactory;
+import org.apache.james.jmap.send.MailFactory;
+import org.apache.james.jmap.send.MailMetadata;
+import org.apache.james.jmap.send.MailSpool;
+import org.apache.james.lifecycle.api.LifecycleUtil;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.mailet.Mail;
+
+import com.google.common.collect.ImmutableList;
+
+public class MessageSender {
+ private final MailSpool mailSpool;
+ private final MailFactory mailFactory;
+
+ @Inject
+ public MessageSender(MailSpool mailSpool, MailFactory mailFactory) {
+ this.mailSpool = mailSpool;
+ this.mailFactory = mailFactory;
+ }
+
+ public void sendMessage(Message jmapMessage,
+ MessageFactory.MetaDataWithContent message,
+ MailboxSession session) throws MailboxException, MessagingException {
+ Mail mail = buildMessage(message, jmapMessage);
+ try {
+ MailMetadata metadata = new MailMetadata(jmapMessage.getId(), session.getUser().getUserName());
+ mailSpool.send(mail, metadata);
+ } finally {
+ LifecycleUtil.dispose(mail);
+ }
+ }
+
+ private Mail buildMessage(MessageFactory.MetaDataWithContent message, Message jmapMessage) throws MessagingException {
+ try {
+ return mailFactory.build(message, jmapMessage);
+ } catch (IOException e) {
+ throw new MessagingException("error building message to send", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/4dbe064d/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
index 8c482e3..8fe1161 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
@@ -21,8 +21,6 @@ package org.apache.james.jmap.methods;
import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
-import java.io.IOException;
-import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -30,7 +28,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.mail.MessagingException;
-import javax.mail.util.SharedByteArrayInputStream;
import org.apache.james.jmap.exceptions.AttachmentsNotFoundException;
import org.apache.james.jmap.methods.ValueWithId.CreationMessageEntry;
@@ -38,8 +35,6 @@ import org.apache.james.jmap.methods.ValueWithId.MessageWithId;
import org.apache.james.jmap.model.Attachment;
import org.apache.james.jmap.model.BlobId;
import org.apache.james.jmap.model.CreationMessage;
-import org.apache.james.jmap.model.CreationMessageId;
-import org.apache.james.jmap.model.Keywords;
import org.apache.james.jmap.model.Message;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent;
@@ -51,11 +46,7 @@ import org.apache.james.jmap.model.SetMessagesRequest;
import org.apache.james.jmap.model.SetMessagesResponse;
import org.apache.james.jmap.model.SetMessagesResponse.Builder;
import org.apache.james.jmap.model.mailbox.Role;
-import org.apache.james.jmap.send.MailFactory;
-import org.apache.james.jmap.send.MailMetadata;
-import org.apache.james.jmap.send.MailSpool;
import org.apache.james.jmap.utils.SystemMailboxesProvider;
-import org.apache.james.lifecycle.api.LifecycleUtil;
import org.apache.james.mailbox.AttachmentManager;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
@@ -64,19 +55,13 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.MailboxNotFoundException;
import org.apache.james.mailbox.model.AttachmentId;
-import org.apache.james.mailbox.model.Cid;
-import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MessageAttachment;
import org.apache.james.metrics.api.MetricFactory;
import org.apache.james.metrics.api.TimeMetric;
-import org.apache.james.util.OptionalUtils;
-import org.apache.mailet.Mail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.fge.lambdas.Throwing;
-import com.github.fge.lambdas.functions.ThrowingFunction;
import com.github.fge.lambdas.predicates.ThrowingPredicate;
import com.github.steveash.guavate.Guavate;
import com.google.common.annotations.VisibleForTesting;
@@ -88,35 +73,29 @@ import com.google.common.collect.ImmutableList;
public class SetMessagesCreationProcessor implements SetMessagesProcessor {
private static final Logger LOG = LoggerFactory.getLogger(SetMailboxesCreationProcessor.class);
- private final MIMEMessageConverter mimeMessageConverter;
- private final MailSpool mailSpool;
- private final MailFactory mailFactory;
private final MessageFactory messageFactory;
private final SystemMailboxesProvider systemMailboxesProvider;
private final AttachmentManager attachmentManager;
private final MetricFactory metricFactory;
private final MailboxManager mailboxManager;
private final MailboxId.Factory mailboxIdFactory;
+ private final MessageAppender messageAppender;
+ private final MessageSender messageSender;
@VisibleForTesting @Inject
- SetMessagesCreationProcessor(MIMEMessageConverter mimeMessageConverter,
- MailSpool mailSpool,
- MailFactory mailFactory,
- MessageFactory messageFactory,
- SystemMailboxesProvider systemMailboxesProvider,
- AttachmentManager attachmentManager,
+ SetMessagesCreationProcessor(MessageFactory messageFactory, SystemMailboxesProvider systemMailboxesProvider,
+ AttachmentManager attachmentManager,
MetricFactory metricFactory,
MailboxManager mailboxManager,
- MailboxId.Factory mailboxIdFactory) {
- this.mimeMessageConverter = mimeMessageConverter;
- this.mailSpool = mailSpool;
- this.mailFactory = mailFactory;
+ MailboxId.Factory mailboxIdFactory, MessageAppender messageAppender, MessageSender messageSender) {
this.messageFactory = messageFactory;
this.systemMailboxesProvider = systemMailboxesProvider;
this.attachmentManager = attachmentManager;
this.metricFactory = metricFactory;
this.mailboxManager = mailboxManager;
this.mailboxIdFactory = mailboxIdFactory;
+ this.messageAppender = messageAppender;
+ this.messageSender = messageSender;
}
@Override
@@ -279,8 +258,10 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
if (!isRequestForSending(entry.getValue(), session)) {
throw new IllegalStateException("Messages for everything but outbox should have been filtered earlier");
}
- MetaDataWithContent newMessage = createMessageInOutbox(entry, outbox, session);
- return sendMessage(entry.getCreationId(), newMessage, session);
+ MetaDataWithContent newMessage = messageAppender.createMessageInMailbox(entry, outbox, session);
+ Message jmapMessage = messageFactory.fromMetaDataWithContent(newMessage);
+ messageSender.sendMessage(jmapMessage, newMessage, session);
+ return new ValueWithId.MessageWithId(entry.getCreationId(), jmapMessage);
}
private boolean isAppendToMailboxWithRole(Role role, CreationMessage entry, MailboxSession mailboxSession) throws MailboxException {
@@ -318,81 +299,5 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
private boolean isRequestForSending(CreationMessage creationMessage, MailboxSession session) throws MailboxException {
return isAppendToMailboxWithRole(Role.OUTBOX, creationMessage, session);
}
-
- private MetaDataWithContent createMessageInOutbox(MessageWithId.CreationMessageEntry createdEntry,
- MessageManager outbox,
- MailboxSession session) throws MailboxException {
- ImmutableList<MessageAttachment> messageAttachments = getMessageAttachments(session, createdEntry.getValue().getAttachments());
- byte[] messageContent = mimeMessageConverter.convert(createdEntry, messageAttachments);
- SharedByteArrayInputStream content = new SharedByteArrayInputStream(messageContent);
- Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
-
- Keywords keywords = createdEntry.getValue()
- .getKeywords()
- .orElse(Keywords.DEFAULT_VALUE);
- boolean notRecent = false;
-
- ComposedMessageId message = outbox.appendMessage(content, internalDate, session, notRecent, keywords.asFlags());
-
- return MetaDataWithContent.builder()
- .uid(message.getUid())
- .keywords(keywords)
- .internalDate(internalDate.toInstant())
- .sharedContent(content)
- .size(messageContent.length)
- .attachments(messageAttachments)
- .mailboxId(outbox.getId())
- .messageId(message.getMessageId())
- .build();
- }
- private ImmutableList<MessageAttachment> getMessageAttachments(MailboxSession session, ImmutableList<Attachment> attachments) throws MailboxException {
- ThrowingFunction<Attachment, Optional<MessageAttachment>> toMessageAttachment = att -> messageAttachment(session, att);
- return attachments.stream()
- .map(Throwing.function(toMessageAttachment).sneakyThrow())
- .flatMap(OptionalUtils::toStream)
- .collect(Guavate.toImmutableList());
- }
-
- private Optional<MessageAttachment> messageAttachment(MailboxSession session, Attachment attachment) throws MailboxException {
- try {
- return Optional.of(MessageAttachment.builder()
- .attachment(attachmentManager.getAttachment(AttachmentId.from(attachment.getBlobId().getRawValue()), session))
- .name(attachment.getName().orElse(null))
- .cid(attachment.getCid().map(Cid::from).orElse(null))
- .isInline(attachment.isIsInline())
- .build());
- } catch (AttachmentNotFoundException e) {
- // should not happen (checked before)
- LOG.error(String.format("Attachment %s not found", attachment.getBlobId()), e);
- return Optional.empty();
- } catch (IllegalStateException e) {
- LOG.error(String.format("Attachment %s is not well-formed", attachment.getBlobId()), e);
- return Optional.empty();
- }
- }
-
- private MessageWithId sendMessage(CreationMessageId creationId, MetaDataWithContent message, MailboxSession session) throws MailboxException, MessagingException {
- Message jmapMessage = messageFactory.fromMetaDataWithContent(message);
- sendMessage(message, jmapMessage, session);
- return new MessageWithId(creationId, jmapMessage);
- }
-
- private void sendMessage(MetaDataWithContent message, Message jmapMessage, MailboxSession session) throws MessagingException {
- Mail mail = buildMessage(message, jmapMessage);
- try {
- MailMetadata metadata = new MailMetadata(jmapMessage.getId(), session.getUser().getUserName());
- mailSpool.send(mail, metadata);
- } finally {
- LifecycleUtil.dispose(mail);
- }
- }
-
- private Mail buildMessage(MetaDataWithContent message, Message jmapMessage) throws MessagingException {
- try {
- return mailFactory.build(message, jmapMessage);
- } catch (IOException e) {
- throw new MessagingException("error building message to send", e);
- }
- }
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/4dbe064d/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
index 8618b11..25ece63 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
@@ -127,6 +127,8 @@ public class SetMessagesCreationProcessorTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
+ private MessageAppender messageAppender;
+ private MessageSender messageSender;
@Before
public void setUp() throws MailboxException {
@@ -146,7 +148,16 @@ public class SetMessagesCreationProcessorTest {
fakeSystemMailboxesProvider = new TestSystemMailboxesProvider(() -> optionalOutbox, () -> optionalDrafts);
session = new MockMailboxSession(USER);
mimeMessageConverter = new MIMEMessageConverter();
- sut = new SetMessagesCreationProcessor(mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory);
+ messageAppender = new MessageAppender(mockedAttachmentManager, mimeMessageConverter);
+ messageSender = new MessageSender(mockedMailSpool, mockedMailFactory);
+ sut = new SetMessagesCreationProcessor(messageFactory,
+ fakeSystemMailboxesProvider,
+ mockedAttachmentManager,
+ new NoopMetricFactory(),
+ mockedMailboxManager,
+ mockedMailboxIdFactory,
+ messageAppender,
+ messageSender);
outbox = mock(MessageManager.class);
when(mockedMailboxIdFactory.fromString(OUTBOX_ID.serialize()))
@@ -230,7 +241,7 @@ public class SetMessagesCreationProcessorTest {
@Test
public void processShouldReturnNonEmptyCreatedWhenRequestHasNonEmptyCreate() throws MailboxException {
// Given
- sut = new SetMessagesCreationProcessor(mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory);
+ sut = new SetMessagesCreationProcessor(messageFactory, fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory, messageAppender, messageSender);
// When
SetMessagesResponse result = sut.process(createMessageInOutbox, session);
@@ -245,7 +256,10 @@ public class SetMessagesCreationProcessorTest {
public void processShouldReturnErrorWhenOutboxNotFound() {
// Given
TestSystemMailboxesProvider doNotProvideOutbox = new TestSystemMailboxesProvider(Optional::empty, () -> optionalDrafts);
- SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, doNotProvideOutbox, mockedAttachmentManager, new NoopMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory);
+ SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(messageFactory, doNotProvideOutbox,
+ mockedAttachmentManager, new NoopMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory,
+ messageAppender,
+ messageSender);
// When
SetMessagesResponse actual = sut.process(createMessageInOutbox, session);
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org