http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java new file mode 100644 index 0000000..a3a068e --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java @@ -0,0 +1,705 @@ +/**************************************************************** + * 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.server.core; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.SequenceInputStream; +import java.util.Enumeration; +import java.util.UUID; + +import javax.activation.DataHandler; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.InternetHeaders; +import javax.mail.internet.MimeMessage; +import javax.mail.util.SharedByteArrayInputStream; + +import org.apache.commons.io.IOUtils; +import org.apache.james.lifecycle.api.Disposable; +import org.apache.james.lifecycle.api.LifecycleUtil; + +/** + * This object wraps a MimeMessage, only loading the underlying MimeMessage + * object when needed. Also tracks if changes were made to reduce unnecessary + * saves. + */ +public class MimeMessageWrapper extends MimeMessage implements Disposable { + + /** + * System property which tells JAMES if it should copy a message in memory + * or via a temporary file. Default is the file + */ + public final static String USE_MEMORY_COPY = "james.message.usememorycopy"; + + /** + * Can provide an input stream to the data + */ + protected MimeMessageSource source = null; + + /** + * This is false until we parse the message + */ + protected boolean messageParsed = false; + + /** + * This is false until we parse the message + */ + protected boolean headersModified = false; + + /** + * This is false until we parse the message + */ + protected boolean bodyModified = false; + + /** + * Keep a reference to the sourceIn so we can close it only when we dispose + * the message. + */ + private InputStream sourceIn; + + private long initialHeaderSize; + + private MimeMessageWrapper(Session session) { + super(session); + this.headers = null; + this.modified = false; + this.headersModified = false; + this.bodyModified = false; + } + + /** + * A constructor that instantiates a MimeMessageWrapper based on a + * MimeMessageSource + * + * @param source + * the MimeMessageSource + * @throws MessagingException + */ + public MimeMessageWrapper(Session session, MimeMessageSource source) { + this(session); + this.source = source; + } + + /** + * A constructor that instantiates a MimeMessageWrapper based on a + * MimeMessageSource + * + * @param source + * the MimeMessageSource + * @throws MessagingException + * @throws MessagingException + */ + public MimeMessageWrapper(MimeMessageSource source) { + this(Session.getDefaultInstance(System.getProperties()), source); + } + + public MimeMessageWrapper(MimeMessage original) throws MessagingException { + this(Session.getDefaultInstance(System.getProperties())); + flags = original.getFlags(); + + if (source == null) { + InputStream in; + + boolean useMemoryCopy = false; + String memoryCopy = System.getProperty(USE_MEMORY_COPY); + if (memoryCopy != null) { + useMemoryCopy = Boolean.valueOf(memoryCopy); + } + try { + + if (useMemoryCopy) { + ByteArrayOutputStream bos; + int size = original.getSize(); + if (size > 0) { + bos = new ByteArrayOutputStream(size); + } else { + bos = new ByteArrayOutputStream(); + } + original.writeTo(bos); + bos.close(); + in = new SharedByteArrayInputStream(bos.toByteArray()); + parse(in); + in.close(); + saved = true; + } else { + MimeMessageInputStreamSource src = new MimeMessageInputStreamSource("MailCopy-" + UUID.randomUUID().toString()); + OutputStream out = src.getWritableOutputStream(); + original.writeTo(out); + out.close(); + source = src; + } + + } catch (IOException ex) { + // should never happen, but just in case... + throw new MessagingException("IOException while copying message", ex); + } + } + } + + /** + * Overrides default javamail behaviour by not altering the Message-ID by + * default, see <a href="https://issues.apache.org/jira/browse/JAMES-875">JAMES-875</a> and + * <a href="https://issues.apache.org/jira/browse/JAMES-1010">JAMES-1010</a> + * + * @see javax.mail.internet.MimeMessage#updateMessageID() + */ + @Override + protected void updateMessageID() throws MessagingException { + if (getMessageID() == null) + super.updateMessageID(); + } + + /** + * Returns the source ID of the MimeMessageSource that is supplying this + * with data. + * + * @see MimeMessageSource + */ + public synchronized String getSourceId() { + return source != null ? source.getSourceId() : null; + } + + /** + * Load the message headers from the internal source. + * + * @throws MessagingException + * if an error is encountered while loading the headers + */ + protected synchronized void loadHeaders() throws MessagingException { + if (headers != null) { + // Another thread has already loaded these headers + } else if (source != null) { + try { + InputStream in = source.getInputStream(); + try { + headers = createInternetHeaders(in); + + } finally { + IOUtils.closeQuietly(in); + } + } catch (IOException ioe) { + throw new MessagingException("Unable to parse headers from stream: " + ioe.getMessage(), ioe); + } + } else { + throw new MessagingException("loadHeaders called for a message with no source, contentStream or stream"); + } + } + + /** + * Load the complete MimeMessage from the internal source. + * + * @throws MessagingException + * if an error is encountered while loading the message + */ + public synchronized void loadMessage() throws MessagingException { + if (messageParsed) { + // Another thread has already loaded this message + } else if (source != null) { + sourceIn = null; + try { + sourceIn = source.getInputStream(); + + parse(sourceIn); + // TODO is it ok? + saved = true; + + } catch (IOException ioe) { + IOUtils.closeQuietly(sourceIn); + sourceIn = null; + throw new MessagingException("Unable to parse stream: " + ioe.getMessage(), ioe); + } + } else { + throw new MessagingException("loadHeaders called for an unparsed message with no source"); + } + } + + /** + * Get whether the message has been modified. + * + * @return whether the message has been modified + */ + public synchronized boolean isModified() { + return headersModified || bodyModified || modified; + } + + /** + * Get whether the body of the message has been modified + * + * @return bodyModified + */ + public synchronized boolean isBodyModified() { + return bodyModified; + } + + /** + * Get whether the header of the message has been modified + * + * @return headersModified + */ + public synchronized boolean isHeaderModified() { + return headersModified; + } + + /** + * Rewritten for optimization purposes + */ + @Override + public void writeTo(OutputStream os) throws IOException, MessagingException { + writeTo(os, os); + + } + + /** + * Rewritten for optimization purposes + */ + @Override + public void writeTo(OutputStream os, String[] ignoreList) throws IOException, MessagingException { + writeTo(os, os, ignoreList); + } + + /** + * Write + */ + public void writeTo(OutputStream headerOs, OutputStream bodyOs) throws IOException, MessagingException { + writeTo(headerOs, bodyOs, new String[0]); + } + + public void writeTo(OutputStream headerOs, OutputStream bodyOs, String[] ignoreList) throws IOException, MessagingException { + writeTo(headerOs, bodyOs, ignoreList, false); + } + + public synchronized void writeTo(OutputStream headerOs, OutputStream bodyOs, String[] ignoreList, boolean preLoad) throws IOException, MessagingException { + + if (!preLoad && source != null && !isBodyModified()) { + // We do not want to instantiate the message... just read from + // source + // and write to this outputstream + + // First handle the headers + InputStream in = source.getInputStream(); + try { + InternetHeaders myHeaders; + MailHeaders parsedHeaders = new MailHeaders(in); + + // check if we should use the parsed headers or not + if (!isHeaderModified()) { + myHeaders = parsedHeaders; + } else { + // The headers was modified so we need to call saveChanges() just to be sure + // See JAMES-1320 + if (!saved) + saveChanges(); + myHeaders = headers; + } + @SuppressWarnings("unchecked") + Enumeration<String> filteredHeaders = myHeaders.getNonMatchingHeaderLines(ignoreList); + IOUtils.copy(new InternetHeadersInputStream(filteredHeaders), headerOs); + IOUtils.copy(in, bodyOs); + } finally { + IOUtils.closeQuietly(in); + } + } else { + // save the changes as the message was modified + // See JAMES-1320 + if (!saved) + saveChanges(); + + // MimeMessageUtil.writeToInternal(this, headerOs, bodyOs, + // ignoreList); + if (headers == null) { + loadHeaders(); + } + @SuppressWarnings("unchecked") + Enumeration<String> filteredHeaders = headers.getNonMatchingHeaderLines(ignoreList); + IOUtils.copy(new InternetHeadersInputStream(filteredHeaders), headerOs); + + if (preLoad && !messageParsed) { + loadMessage(); + } + MimeMessageUtil.writeMessageBodyTo(this, bodyOs); + } + } + + /** + * This is the MimeMessage implementation - this should return ONLY the + * body, not the entire message (should not count headers). This size will + * never change on {@link #saveChanges()} + */ + @Override + public synchronized int getSize() throws MessagingException { + if (source != null) { + try { + long fullSize = source.getMessageSize(); + if (headers == null) { + loadHeaders(); + } + // 2 == CRLF + return (int) (fullSize - initialHeaderSize - 2); + + } catch (IOException e) { + throw new MessagingException("Unable to calculate message size"); + } + } else { + if (!messageParsed) { + loadMessage(); + } + + return super.getSize(); + } + + } + + /** + * Corrects JavaMail 1.1 version which always returns -1. Only corrected for + * content less than 5000 bytes, to avoid memory hogging. + */ + @Override + public int getLineCount() throws MessagingException { + InputStream in; + try { + in = getContentStream(); + } catch (Exception e) { + return -1; + } + if (in == null) { + return -1; + } + // Wrap input stream in LineNumberReader + // Not sure what encoding to use really... + InputStreamReader isr = null; + LineNumberReader counter = null; + try { + if (getEncoding() != null) { + isr = new InputStreamReader(in, getEncoding()); + counter = new LineNumberReader(isr); + } else { + isr = new InputStreamReader(in); + counter = new LineNumberReader(isr); + } + // Read through all the data + char[] block = new char[4096]; + while (counter.read(block) > -1) { + // Just keep reading + } + return counter.getLineNumber(); + } catch (IOException ioe) { + return -1; + } finally { + IOUtils.closeQuietly(counter); + IOUtils.closeQuietly(isr); + IOUtils.closeQuietly(in); + } + } + + /** + * Returns size of message, ie headers and content + */ + public long getMessageSize() throws MessagingException { + if (source != null && !isModified()) { + try { + return source.getMessageSize(); + } catch (IOException ioe) { + throw new MessagingException("Error retrieving message size", ioe); + } + } else { + return MimeMessageUtil.calculateMessageSize(this); + } + } + + /** + * We override all the "headers" access methods to be sure that we loaded + * the headers + */ + + @Override + public String[] getHeader(String name) throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getHeader(name); + } + + @Override + public String getHeader(String name, String delimiter) throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getHeader(name, delimiter); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getAllHeaders() throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getAllHeaders(); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getMatchingHeaders(String[] names) throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getMatchingHeaders(names); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getNonMatchingHeaders(String[] names) throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getNonMatchingHeaders(names); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getAllHeaderLines() throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getAllHeaderLines(); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getMatchingHeaderLines(String[] names) throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getMatchingHeaderLines(names); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getNonMatchingHeaderLines(String[] names) throws MessagingException { + if (headers == null) { + loadHeaders(); + } + return headers.getNonMatchingHeaderLines(names); + } + + private synchronized void checkModifyHeaders() throws MessagingException { + // Disable only-header loading optimizations for JAMES-559 + /* + * if (!messageParsed) { loadMessage(); } + */ + + // End JAMES-559 + if (headers == null) { + loadHeaders(); + } + modified = true; + saved = false; + headersModified = true; + } + + @Override + public void setHeader(String name, String value) throws MessagingException { + checkModifyHeaders(); + super.setHeader(name, value); + } + + @Override + public void addHeader(String name, String value) throws MessagingException { + checkModifyHeaders(); + super.addHeader(name, value); + } + + @Override + public void removeHeader(String name) throws MessagingException { + checkModifyHeaders(); + super.removeHeader(name); + } + + @Override + public void addHeaderLine(String line) throws MessagingException { + checkModifyHeaders(); + super.addHeaderLine(line); + } + + /** + * The message is changed when working with headers and when altering the + * content. Every method that alter the content will fallback to this one. + * + * @see javax.mail.Part#setDataHandler(javax.activation.DataHandler) + */ + @Override + public synchronized void setDataHandler(DataHandler arg0) throws MessagingException { + modified = true; + saved = false; + bodyModified = true; + super.setDataHandler(arg0); + } + + @Override + public void dispose() { + if (sourceIn != null) { + IOUtils.closeQuietly(sourceIn); + } + if (source != null) { + LifecycleUtil.dispose(source); + } + } + + /** + * @see javax.mail.internet.MimeMessage#parse(java.io.InputStream) + */ + @Override + protected synchronized void parse(InputStream is) throws MessagingException { + // the super implementation calls + // headers = createInternetHeaders(is); + super.parse(is); + messageParsed = true; + } + + /** + * If we already parsed the headers then we simply return the updated ones. + * Otherwise we parse + * + * @see javax.mail.internet.MimeMessage#createInternetHeaders(java.io.InputStream) + */ + @Override + protected synchronized InternetHeaders createInternetHeaders(InputStream is) throws MessagingException { + /* + * This code is no more needed: see JAMES-570 and new tests + * + * InternetHeaders can be a bit awkward to work with due to its own + * internal handling of header order. This hack may not always be + * necessary, but for now we are trying to ensure that there is a + * Return-Path header, even if just a placeholder, so that later, e.g., + * in LocalDelivery, when we call setHeader, it will remove any other + * Return-Path headers, and ensure that ours is on the top. addHeader + * handles header order, but not setHeader. This may change in future + * JavaMail. But if there are other Return-Path header values, let's + * drop our placeholder. + * + * MailHeaders newHeaders = new MailHeaders(new + * ByteArrayInputStream((f.RETURN_PATH + ": placeholder").getBytes())); + * newHeaders.setHeader(RFC2822Headers.RETURN_PATH, null); + * newHeaders.load(is); String[] returnPathHeaders = + * newHeaders.getHeader(RFC2822Headers.RETURN_PATH); if + * (returnPathHeaders.length > 1) + * newHeaders.setHeader(RFC2822Headers.RETURN_PATH, + * returnPathHeaders[1]); + */ + + // Keep this: skip the headers from the stream + // we could put that code in the else and simple write an "header" + // skipping + // reader for the others. + MailHeaders newHeaders = new MailHeaders(is); + + if (headers != null) { + return headers; + } else { + initialHeaderSize = newHeaders.getSize(); + + return newHeaders; + } + } + + /** + * @see javax.mail.internet.MimeMessage#getContentStream() + */ + @Override + protected InputStream getContentStream() throws MessagingException { + if (!messageParsed) { + loadMessage(); + } + return super.getContentStream(); + } + + /** + * @see javax.mail.internet.MimeMessage#getRawInputStream() + */ + @Override + public synchronized InputStream getRawInputStream() throws MessagingException { + if (!messageParsed && !isModified() && source != null) { + InputStream is; + try { + is = source.getInputStream(); + // skip the headers. + new MailHeaders(is); + return is; + } catch (IOException e) { + throw new MessagingException("Unable to read the stream: " + e.getMessage(), e); + } + } else { + return super.getRawInputStream(); + } + } + + /** + * Return an {@link InputStream} which holds the full content of the + * message. This method tries to optimize this call as far as possible. This + * stream contains the updated {@link MimeMessage} content if something was + * changed + * + * @return messageInputStream + * @throws MessagingException + */ + + public synchronized InputStream getMessageInputStream() throws MessagingException { + if (!messageParsed && !isModified() && source != null) { + try { + return source.getInputStream(); + } catch (IOException e) { + throw new MessagingException("Unable to get inputstream", e); + } + } else { + try { + + // Try to optimize if possible to prevent OOM on big mails. + // See JAMES-1252 for an example + if (!bodyModified && source != null) { + // ok only the headers were modified so we don't need to + // copy the whole message content into memory + InputStream in = source.getInputStream(); + + // skip over headers from original stream we want to use the + // in memory ones + new MailHeaders(in); + + // now construct the new stream using the in memory headers + // and the body from the original source + return new SequenceInputStream(new InternetHeadersInputStream(getAllHeaderLines()), in); + } else { + // the body was changed so we have no other solution to copy + // it into memory first :( + ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeTo(out); + return new ByteArrayInputStream(out.toByteArray()); + } + } catch (IOException e) { + throw new MessagingException("Unable to get inputstream", e); + } + } + } + +}
http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ClassPathResource.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ClassPathResource.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ClassPathResource.java new file mode 100644 index 0000000..d7c5378 --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ClassPathResource.java @@ -0,0 +1,110 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +public class ClassPathResource implements Resource { + + private final String path; + private final ClassLoader classLoader; + + public ClassPathResource(String path) { + this.path = sanitizePath(path); + this.classLoader = getDefaultClassLoader(); + } + + private String sanitizePath(String path) { + String pathToUse = new SimpleUrl(path).getSimplified(); + if (pathToUse.startsWith("/")) { + return pathToUse.substring(1); + } + return pathToUse; + } + + @Override + public File getFile() throws IOException { + URL url = getURL(); + return ResourceUtils.getFile(url, getDescription()); + } + + public URL getURL() throws IOException { + URL url = resolveURL(); + if (url == null) { + throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist"); + } + return url; + } + + protected URL resolveURL() { + return this.classLoader.getResource(this.path); + } + + @Override + public InputStream getInputStream() throws IOException { + InputStream is = this.classLoader.getResourceAsStream(this.path); + if (is == null) { + throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); + } + return is; + } + + public String getDescription() { + return "class path resource [" + path + "]"; + } + + private ClassLoader getDefaultClassLoader() { + ClassLoader currentThreadClassLoader = getcurrentThreadClassLoader(); + if (currentThreadClassLoader != null) { + return currentThreadClassLoader; + } + + // No thread context class loader -> use class loader of this class. + ClassLoader currentClassClassLoader = ClassPathResource.class.getClassLoader(); + if (currentClassClassLoader != null) { + return currentClassClassLoader; + } + + // getClassLoader() returning null indicates the bootstrap ClassLoader + return getSystemClassLoader(); + } + + private ClassLoader getcurrentThreadClassLoader() { + try { + return Thread.currentThread().getContextClassLoader(); + } catch (Throwable ex) { + // Cannot access thread context ClassLoader - falling back... + return null; + } + } + + private ClassLoader getSystemClassLoader() { + try { + return ClassLoader.getSystemClassLoader(); + } catch (Throwable ex) { + // Cannot access system ClassLoader - oh well, maybe the + // caller can live with null... + return null; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemImpl.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemImpl.java new file mode 100644 index 0000000..a068f86 --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemImpl.java @@ -0,0 +1,60 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import javax.inject.Inject; + +import org.apache.james.filesystem.api.FileSystem; +import org.apache.james.filesystem.api.JamesDirectoriesProvider; + +public class FileSystemImpl implements FileSystem { + + private final JamesDirectoriesProvider directoryProvider; + private final ResourceFactory resourceLoader; + + @Inject + public FileSystemImpl(JamesDirectoriesProvider directoryProvider) { + this.directoryProvider = directoryProvider; + this.resourceLoader = new ResourceFactory(directoryProvider); + } + + @Override + public File getBasedir() throws FileNotFoundException { + return new File(directoryProvider.getRootDirectory()); + } + + @Override + public InputStream getResource(String url) throws IOException { + return resourceLoader.getResource(url).getInputStream(); + } + + @Override + public File getFile(String fileURL) throws FileNotFoundException { + try { + return resourceLoader.getResource(fileURL).getFile(); + } catch (IOException e) { + throw new FileNotFoundException(e.getMessage()); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemResource.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemResource.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemResource.java new file mode 100644 index 0000000..dd90612 --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/FileSystemResource.java @@ -0,0 +1,44 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +class FileSystemResource implements Resource { + + private final File file; + + public FileSystemResource(File file) { + this.file = file; + } + + @Override + public File getFile() { + return this.file; + } + + @Override + public InputStream getInputStream() throws IOException { + return new FileInputStream(this.file); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/Resource.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/Resource.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/Resource.java new file mode 100644 index 0000000..d78abdd --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/Resource.java @@ -0,0 +1,29 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public interface Resource { + File getFile() throws IOException; + + InputStream getInputStream() throws IOException; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceFactory.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceFactory.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceFactory.java new file mode 100644 index 0000000..0cb9a18 --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceFactory.java @@ -0,0 +1,100 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.james.filesystem.api.FileSystem; +import org.apache.james.filesystem.api.JamesDirectoriesProvider; + +public class ResourceFactory { + + private final JamesDirectoriesProvider directoryProvider; + + public ResourceFactory(JamesDirectoriesProvider directoryProvider) { + this.directoryProvider = directoryProvider; + } + + public Resource getResource(String fileURL) { + if (fileURL.startsWith(FileSystem.CLASSPATH_PROTOCOL)) { + return handleClasspathProtocol(fileURL); + } else if (fileURL.startsWith(FileSystem.FILE_PROTOCOL)) { + return handleFileProtocol(fileURL); + } else { + try { + // Try to parse the location as a URL... + return handleUrlResource(fileURL); + } catch (MalformedURLException ex) { + // No URL -> resolve as resource path. + return new ClassPathResource(fileURL); + } + } + } + + private Resource handleUrlResource(String fileURL) throws MalformedURLException { + URL url = new URL(fileURL); + return new UrlResource(url); + } + + private Resource handleClasspathProtocol(String fileURL) { + String resourceName = fileURL.substring(FileSystem.CLASSPATH_PROTOCOL.length()); + return new ClassPathResource(resourceName); + } + + private Resource handleFileProtocol(String fileURL) { + File file = interpretPath(fileURL); + return new FileSystemResource(file); + } + + private File interpretPath(String fileURL) { + if (FileProtocol.CONF.match(fileURL)) { + return new File(directoryProvider.getConfDirectory() + "/" + FileProtocol.CONF.removeProtocolFromPath(fileURL)); + } else if (FileProtocol.VAR.match(fileURL)) { + return new File(directoryProvider.getVarDirectory() + "/" + FileProtocol.VAR.removeProtocolFromPath(fileURL)); + } else if (FileProtocol.ABSOLUTE.match(fileURL)) { + return new File(directoryProvider.getAbsoluteDirectory() + FileProtocol.ABSOLUTE.removeProtocolFromPath(fileURL)); + } else { + // move to the root folder of the spring deployment + return new File(directoryProvider.getRootDirectory() + "/" + FileProtocol.OTHER.removeProtocolFromPath(fileURL)); + } + } + + private enum FileProtocol { + CONF(FileSystem.FILE_PROTOCOL_AND_CONF), + VAR(FileSystem.FILE_PROTOCOL_AND_VAR), + ABSOLUTE(FileSystem.FILE_PROTOCOL_ABSOLUTE), + OTHER(FileSystem.FILE_PROTOCOL); + + private final String protocolPrefix; + + FileProtocol(String protocolPrefix) { + this.protocolPrefix = protocolPrefix; + } + + private boolean match(String path) { + return path.startsWith(protocolPrefix); + } + + private String removeProtocolFromPath(String path) { + return path.substring(protocolPrefix.length()); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceUtils.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceUtils.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceUtils.java new file mode 100644 index 0000000..b59336f --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/ResourceUtils.java @@ -0,0 +1,41 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.net.URL; + +public class ResourceUtils { + public static final String URL_PROTOCOL_FILE = "file"; + + public static File getFile(URL url, String description) throws FileNotFoundException { + if (!URL_PROTOCOL_FILE.equals(url.getProtocol())) { + throw new FileNotFoundException(description + " cannot be resolved to absolute file path " + "because it does not reside in the file system: " + url); + } + try { + return new File(url.toURI().getSchemeSpecificPart()); + } catch (URISyntaxException ex) { + // Fallback for URLs that are not valid URIs (should hardly ever + // happen). + return new File(url.getFile()); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/SimpleUrl.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/SimpleUrl.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/SimpleUrl.java new file mode 100644 index 0000000..dfe3b3b --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/SimpleUrl.java @@ -0,0 +1,74 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.CharMatcher; +import com.google.common.io.Files; + +public class SimpleUrl { + private static final String FOLDER_SEPARATOR = "/"; + + private static final char WINDOWS_FOLDER_SEPARATOR = '\\'; + + private static final String CURRENT_PATH = "."; + + private static final Pattern URL_REGEXP = Pattern.compile("^([^/][^/]*:(?://)?)?(.*)"); + + private static String url; + private static String protocol; + private static String path; + private static String simplifiedUrl; + + public SimpleUrl(String url) { + SimpleUrl.url = url; + String urlWithUnixSeparators = CharMatcher.is(WINDOWS_FOLDER_SEPARATOR).replaceFrom(url, FOLDER_SEPARATOR); + extractComponents(urlWithUnixSeparators); + simplifiedUrl = protocol + simplifyPath(path); + } + + private static void extractComponents(String urlWithUnixSeparators) { + Matcher m = URL_REGEXP.matcher(urlWithUnixSeparators); + m.matches(); + protocol = Optional.ofNullable(m.group(1)).orElse(""); + path = Optional.ofNullable(m.group(2)).orElse(""); + } + + @VisibleForTesting + static String simplifyPath(String path) { + String simplified = Files.simplifyPath(path); + if (CURRENT_PATH.equals(simplified)) { + return ""; + } + return simplified; + } + + public String getUrl() { + return url; + } + + public String getSimplified() { + return simplifiedUrl; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/filesystem/UrlResource.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/filesystem/UrlResource.java b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/UrlResource.java new file mode 100644 index 0000000..781af4d --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/filesystem/UrlResource.java @@ -0,0 +1,59 @@ +/**************************************************************** + * 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.server.core.filesystem; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +public class UrlResource implements Resource { + public static final String URL_PROTOCOL_FILE = "file"; + private final URL url; + + public UrlResource(URL url) { + this.url = url; + } + + @Override + public InputStream getInputStream() throws IOException { + URLConnection con = this.url.openConnection(); + useCachesIfNecessary(con); + try { + return con.getInputStream(); + } catch (IOException ex) { + // Close the HTTP connection (if applicable). + if (con instanceof HttpURLConnection) { + ((HttpURLConnection) con).disconnect(); + } + throw ex; + } + } + + public static void useCachesIfNecessary(URLConnection con) { + con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP")); + } + + @Override + public File getFile() throws IOException { + return ResourceUtils.getFile(url, "URL [" + this.url + "]"); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/main/java/org/apache/james/server/core/package.html ---------------------------------------------------------------------- diff --git a/server/container/core/src/main/java/org/apache/james/server/core/package.html b/server/container/core/src/main/java/org/apache/james/server/core/package.html new file mode 100644 index 0000000..a9ce61d --- /dev/null +++ b/server/container/core/src/main/java/org/apache/james/server/core/package.html @@ -0,0 +1,21 @@ +<!-- + 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. +--> +<body> +<p>Implementations of core James services and concepts.</p> +</body> http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MailHeadersTest.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MailHeadersTest.java b/server/container/core/src/test/java/org/apache/james/core/MailHeadersTest.java deleted file mode 100644 index 0a24dd8..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MailHeadersTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************** - * 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.core; - -import java.io.ByteArrayInputStream; -import java.util.Enumeration; -import javax.mail.MessagingException; -import org.apache.mailet.base.RFC2822Headers; -import static org.junit.Assert.assertEquals; -import org.junit.Test; - -public class MailHeadersTest { - - @Test - public void testHeadersOrder() throws MessagingException { - MailHeaders header = new MailHeaders(new ByteArrayInputStream((RFC2822Headers.SUBJECT + ": testsubject\r\n"). - getBytes())); - header.setHeader(RFC2822Headers.RETURN_PATH, "<test@test>"); - header.setHeader(RFC2822Headers.FROM, "<te...@test.de>"); - @SuppressWarnings("unchecked") - Enumeration<String> h = header.getAllHeaderLines(); - - assertEquals(h.nextElement(), "Return-Path: <test@test>"); - assertEquals(h.nextElement(), "From: <te...@test.de>"); - assertEquals(h.nextElement(), "Subject: testsubject"); - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MailImplTest.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MailImplTest.java b/server/container/core/src/test/java/org/apache/james/core/MailImplTest.java deleted file mode 100644 index aa82d0b..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MailImplTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************** - * 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.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; - -import java.io.ByteArrayInputStream; -import java.util.ArrayList; - -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.internet.MimeMessage; - -import org.apache.mailet.Mail; -import org.apache.mailet.MailAddress; -import org.apache.mailet.base.test.MailUtil; -import org.junit.Test; - -public class MailImplTest extends MailTestAllImplementations { - - private static final Session NO_SESSION = null; - - @Override - protected Mail createMailImplementation() { - return new MailImpl(); - } - - @Test - public void testConstr1() throws MessagingException { - MailImpl mail = new MailImpl(); - - helperTestInitialState(mail); - helperTestMessageSize(mail, 0); // MimeMessageWrapper default is 0 - assertNull("no initial message", mail.getMessage()); - assertNull("no initial sender", mail.getSender()); - assertNull("no initial name", mail.getName()); - } - - @Test - public void testConstr2() throws MessagingException { - ArrayList<MailAddress> recepients = new ArrayList<>(); - String name = MailUtil.newId(); - String sender = "sender@localhost"; - MailAddress senderMailAddress = new MailAddress(sender); - MailImpl mail = new MailImpl(name, senderMailAddress, recepients); - - helperTestInitialState(mail); // MimeMessageWrapper default is 0 - helperTestMessageSize(mail, 0); // MimeMessageWrapper default is 0 - assertNull("no initial message", mail.getMessage()); - assertEquals("sender", sender, mail.getSender().toString()); - assertEquals("name", name, mail.getName()); - - mail.setMessage(new MimeMessage(NO_SESSION)); - assertNotNull("message", mail.getMessage()); - } - - @Test - public void testConstr3() throws MessagingException { - ArrayList<MailAddress> recepients = new ArrayList<>(); - String name = MailUtil.newId(); - String sender = "sender@localhost"; - MailAddress senderMailAddress = new MailAddress(sender); - MimeMessage mimeMessage = new MimeMessage(NO_SESSION, new ByteArrayInputStream(new byte[0])); - MailImpl mail = new MailImpl(name, senderMailAddress, recepients, mimeMessage); - - helperTestInitialState(mail); - helperTestMessageSize(mail, 0); - assertEquals("initial message", mimeMessage.getMessageID(), mail.getMessage().getMessageID()); - assertEquals("sender", sender, mail.getSender().toString()); - assertEquals("name", name, mail.getName()); - mail.dispose(); - } - - @Test - public void testDuplicate() throws MessagingException { - MailImpl mail = new MailImpl(); - MailImpl duplicate = (MailImpl) mail.duplicate(); - assertNotSame("is real duplicate", mail, duplicate); - helperTestInitialState(duplicate); - helperTestMessageSize(duplicate, 0); - } - - @Test - public void testDuplicateNewName() throws MessagingException { - String newName = "aNewName"; - - MailImpl mail = new MailImpl(); - assertFalse("before + after names differ", newName.equals(mail.getName())); - - MailImpl duplicate = (MailImpl) mail.duplicate(newName); - assertEquals("new name set", newName, duplicate.getName()); - helperTestInitialState(duplicate); - helperTestMessageSize(duplicate, 0); - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MailTestAllImplementations.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MailTestAllImplementations.java b/server/container/core/src/test/java/org/apache/james/core/MailTestAllImplementations.java deleted file mode 100644 index 9e928f7..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MailTestAllImplementations.java +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************** - * 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.core; - -import javax.mail.MessagingException; - -import org.apache.mailet.Mail; -import org.junit.Test; -import static org.junit.Assert.*; - -/** - * testing common behavior of Mail implementors. subclasses automatically get - * their Mail-behavior tested. - */ -public abstract class MailTestAllImplementations { - - /** provide the concrete implementation to test */ - protected abstract Mail createMailImplementation(); - - protected void helperTestInitialState(Mail mail) { - assertFalse("no initial attributes", mail.hasAttributes()); - assertNull("no initial error", mail.getErrorMessage()); - assertNotNull("initial last update set", mail.getLastUpdated()); - try { - assertTrue("no initial recipient", mail.getRecipients().isEmpty()); - } catch (NullPointerException e) { - // current behavior. *BUT*, shouldn't this method better return with - // an empty list?! - } - assertEquals("initial remote address is localhost ip", "127.0.0.1", mail.getRemoteAddr()); - assertEquals("initial remote host is localhost", "localhost", mail.getRemoteHost()); - assertEquals("default initial state", Mail.DEFAULT, mail.getState()); - } - - protected void helperTestMessageSize(Mail mail, int expectedMsgSize) throws MessagingException { - try { - assertEquals("initial message size == " + expectedMsgSize, expectedMsgSize, mail.getMessageSize()); - } catch (NullPointerException e) { - // current behavior. *BUT*, shouldn't this method return more - // gracefully?! - } - } - - @Test - public void testAttributes() { - Mail mail = createMailImplementation(); - assertFalse("no initial attributes", mail.hasAttributes()); - assertFalse("attributes initially empty", mail.getAttributeNames().hasNext()); - assertNull("not found on emtpy list", mail.getAttribute("test")); - assertNull("no previous item with key", mail.setAttribute("testKey", "testValue")); - assertEquals("item found", "testValue", mail.getAttribute("testKey")); - assertTrue("has attribute", mail.hasAttributes()); - assertEquals("item removed", "testValue", mail.removeAttribute("testKey")); - assertNull("item no longer found", mail.getAttribute("testKey")); - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MimeMessageCopyOnWriteProxyTest.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MimeMessageCopyOnWriteProxyTest.java b/server/container/core/src/test/java/org/apache/james/core/MimeMessageCopyOnWriteProxyTest.java deleted file mode 100644 index 36c678b..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MimeMessageCopyOnWriteProxyTest.java +++ /dev/null @@ -1,279 +0,0 @@ -/**************************************************************** - * 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.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.Properties; - -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.internet.MimeMessage; -import javax.mail.util.SharedByteArrayInputStream; - -import org.apache.james.lifecycle.api.LifecycleUtil; -import org.apache.mailet.Mail; -import org.apache.mailet.MailAddress; -import org.junit.Test; - -public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest { - - final String content = "Subject: foo\r\nContent-Transfer-Encoding2: plain"; - final String sep = "\r\n\r\n"; - final String body = "bar\r\n.\r\n"; - - @Override - protected MimeMessage getMessageFromSources(String sources) throws Exception { - MimeMessageInputStreamSource mmis = new MimeMessageInputStreamSource("test", new SharedByteArrayInputStream(sources.getBytes())); - return new MimeMessageCopyOnWriteProxy(mmis); - // return new MimeMessage(Session.getDefaultInstance(new - // Properties()),new ByteArrayInputStream(sources.getBytes())); - } - - @Test - public void testMessageCloning1() throws Exception { - ArrayList<MailAddress> r = new ArrayList<>(); - r.add(new MailAddress("recipi...@test.com")); - MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources( - content + sep + body); - MailImpl mail = new MailImpl("test", new MailAddress("t...@test.com"), r, messageFromSources); - MailImpl m2 = (MailImpl) mail.duplicate(); - System.out.println("mail: " + getReferences(mail.getMessage()) + " m2: " + getReferences(m2.getMessage())); - assertNotSame(m2, mail); - assertNotSame(m2.getMessage(), mail.getMessage()); - // test that the wrapped message is the same - assertTrue(isSameMimeMessage(m2.getMessage(), mail.getMessage())); - // test it is the same after read only operations! - mail.getMessage().getSubject(); - assertTrue(isSameMimeMessage(m2.getMessage(), mail.getMessage())); - mail.getMessage().setText("new body"); - mail.getMessage().saveChanges(); - // test it is different after a write operation! - mail.getMessage().setSubject("new Subject"); - assertTrue(!isSameMimeMessage(m2.getMessage(), mail.getMessage())); - LifecycleUtil.dispose(mail); - LifecycleUtil.dispose(m2); - LifecycleUtil.dispose(messageFromSources); - } - - @Test - public void testMessageCloning2() throws Exception { - ArrayList<MailAddress> r = new ArrayList<>(); - r.add(new MailAddress("recipi...@test.com")); - MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources( - content + sep + body); - MailImpl mail = new MailImpl("test", new MailAddress("t...@test.com"), r, messageFromSources); - MailImpl m2 = (MailImpl) mail.duplicate(); - System.out.println("mail: " + getReferences(mail.getMessage()) + " m2: " + getReferences(m2.getMessage())); - assertNotSame(m2, mail); - assertNotSame(m2.getMessage(), mail.getMessage()); - // test that the wrapped message is the same - assertTrue(isSameMimeMessage(m2.getMessage(), mail.getMessage())); - // test it is the same after real only operations! - m2.getMessage().getSubject(); - assertTrue(isSameMimeMessage(m2.getMessage(), mail.getMessage())); - m2.getMessage().setText("new body"); - m2.getMessage().saveChanges(); - // test it is different after a write operation! - m2.getMessage().setSubject("new Subject"); - assertTrue(!isSameMimeMessage(m2.getMessage(), mail.getMessage())); - // check that the subjects are correct on both mails! - assertEquals(m2.getMessage().getSubject(), "new Subject"); - assertEquals(mail.getMessage().getSubject(), "foo"); - // cloning again the messages - Mail m2clone = m2.duplicate(); - assertTrue(isSameMimeMessage(m2clone.getMessage(), m2.getMessage())); - MimeMessage mm = getWrappedMessage(m2.getMessage()); - assertNotSame(m2.getMessage(), m2clone.getMessage()); - // test that m2clone has a valid wrapped message - MimeMessage mm3 = getWrappedMessage(m2clone.getMessage()); - assertNotNull(mm3); - // dispose m2 and check that the clone has still a valid message and it - // is the same! - LifecycleUtil.dispose(m2); - assertEquals(mm3, getWrappedMessage(m2clone.getMessage())); - // change the message that should be not referenced by m2 that has - // been disposed, so it should not clone it! - m2clone.getMessage().setSubject("new Subject 2"); - m2clone.getMessage().setText("new Body 3"); - assertTrue(isSameMimeMessage(m2clone.getMessage(), mm)); - LifecycleUtil.dispose(mail); - LifecycleUtil.dispose(messageFromSources); - } - - /** - * If I create a new MimeMessageCopyOnWriteProxy from another - * MimeMessageCopyOnWriteProxy, I remove references to the first and I - * change the second, then it should not clone - */ - @Test - public void testMessageAvoidCloning() throws Exception { - ArrayList<MailAddress> r = new ArrayList<>(); - r.add(new MailAddress("recipi...@test.com")); - MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources( - content + sep + body); - MailImpl mail = new MailImpl("test", new MailAddress("t...@test.com"), r, messageFromSources); - // cloning the message - Mail mailClone = mail.duplicate(); - assertTrue(isSameMimeMessage(mailClone.getMessage(), mail.getMessage())); - MimeMessage mm = getWrappedMessage(mail.getMessage()); - assertNotSame(mail.getMessage(), mailClone.getMessage()); - // dispose mail and check that the clone has still a valid message and - // it is the same! - LifecycleUtil.dispose(mail); - LifecycleUtil.dispose(messageFromSources); - // need to add a gc and a wait, because the original mimemessage should - // be finalized before the test. - System.gc(); - Thread.sleep(1000); - // dumb test - assertTrue(isSameMimeMessage(mailClone.getMessage(), mailClone.getMessage())); - // change the message that should be not referenced by mail that has - // been disposed, so it should not clone it! - mailClone.getMessage().setSubject("new Subject 2"); - mailClone.getMessage().setText("new Body 3"); - assertTrue(isSameMimeMessage(mailClone.getMessage(), mm)); - LifecycleUtil.dispose(mailClone); - LifecycleUtil.dispose(mm); - } - - /** - * If I create a new MimeMessageCopyOnWriteProxy from a MimeMessage and I - * change the new message, the original should be unaltered and the proxy - * should clone the message. - */ - @Test - public void testMessageCloning3() throws Exception { - ArrayList<MailAddress> r = new ArrayList<>(); - r.add(new MailAddress("recipi...@test.com")); - MimeMessage m = new MimeMessage(Session.getDefaultInstance(new Properties(null))); - m.setText("CIPS"); - MailImpl mail = new MailImpl("test", new MailAddress("t...@test.com"), r, m); - assertTrue(isSameMimeMessage(m, mail.getMessage())); - // change the message that should be not referenced by mail that has - // been disposed, so it should not clone it! - System.gc(); - Thread.sleep(100); - mail.getMessage().setSubject("new Subject 2"); - mail.getMessage().setText("new Body 3"); - System.gc(); - Thread.sleep(100); - assertFalse(isSameMimeMessage(m, mail.getMessage())); - LifecycleUtil.dispose(mail); - LifecycleUtil.dispose(m); - } - - @Test - public void testMessageDisposing() throws Exception { - ArrayList<MailAddress> r = new ArrayList<>(); - r.add(new MailAddress("recipi...@test.com")); - MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources( - content + sep + body); - MailImpl mail = new MailImpl("test", new MailAddress("t...@test.com"), r, messageFromSources); - // cloning the message - MailImpl mailClone = (MailImpl) mail.duplicate(); - LifecycleUtil.dispose(mail); - - assertNotNull(getWrappedMessage(mailClone.getMessage())); - assertNull(mail.getMessage()); - - LifecycleUtil.dispose(mailClone); - - assertNull(mailClone.getMessage()); - assertNull(mail.getMessage()); - LifecycleUtil.dispose(mail); - LifecycleUtil.dispose(messageFromSources); - } - - @Test - public void testNPE1() throws MessagingException, InterruptedException { - ArrayList<MailAddress> recipients = new ArrayList<>(); - recipients.add(new MailAddress("recipi...@test.com")); - MimeMessageCopyOnWriteProxy mw = new MimeMessageCopyOnWriteProxy(new MimeMessageInputStreamSource("test", - new SharedByteArrayInputStream(("Return-path: ret...@test.com\r\n" + "Content-Transfer-Encoding: plain\r\n" + "Subject: test\r\n\r\n" + "Body Text testNPE1\r\n"). - getBytes()))); - - MimeMessageCopyOnWriteProxy mw2 = new MimeMessageCopyOnWriteProxy(mw); - LifecycleUtil.dispose(mw2); - mw2 = null; - System.gc(); - Thread.sleep(1000); - // the NPE was inside this call - mw.getMessageSize(); - LifecycleUtil.dispose(mw); - } - - /** - * This test throw a NullPointerException when the original message was - * created by a MimeMessageInputStreamSource. - */ - @Test - public void testMessageCloningViaCoW3() throws Exception { - MimeMessage mmorig = getSimpleMessage(); - - MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig); - - LifecycleUtil.dispose(mmorig); - mmorig = null; - System.gc(); - Thread.sleep(200); - - try { - mm.writeTo(System.out); - } catch (Exception e) { - e.printStackTrace(); - fail("Exception while writing the message to output"); - } - - LifecycleUtil.dispose(mm); - } - - private static String getReferences(MimeMessage m) { - StringBuilder ref = new StringBuilder("/"); - while (m instanceof MimeMessageCopyOnWriteProxy) { - ref.append(((MimeMessageCopyOnWriteProxy) m).refCount.getReferenceCount()).append("/"); - m = ((MimeMessageCopyOnWriteProxy) m).getWrappedMessage(); - } - if (m instanceof MimeMessageWrapper) { - ref.append("W"); - } else { - ref.append("M"); - } - return ref.toString(); - } - - private static MimeMessage getWrappedMessage(MimeMessage m) { - while (m instanceof MimeMessageCopyOnWriteProxy) { - m = ((MimeMessageCopyOnWriteProxy) m).getWrappedMessage(); - } - return m; - } - - private static boolean isSameMimeMessage(MimeMessage first, MimeMessage second) { - return getWrappedMessage(first) == getWrappedMessage(second); - - } -} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromMimeMessageTest.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromMimeMessageTest.java b/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromMimeMessageTest.java deleted file mode 100644 index b554576..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromMimeMessageTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/**************************************************************** - * 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.core; - -import javax.mail.internet.MimeMessage; - -public class MimeMessageFromMimeMessageTest extends MimeMessageFromStreamTest { - - @Override - protected MimeMessage getMessageFromSources(String sources) throws Exception { - return new MimeMessage(super.getMessageFromSources(sources)); - } - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromSharedStreamTest.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromSharedStreamTest.java b/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromSharedStreamTest.java deleted file mode 100644 index ac61aa1..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromSharedStreamTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/**************************************************************** - * 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.core; - -import javax.mail.util.SharedByteArrayInputStream; - -import javax.mail.Session; -import javax.mail.internet.MimeMessage; - -import java.util.Properties; - -public class MimeMessageFromSharedStreamTest extends MimeMessageFromStreamTest { - - @Override - protected MimeMessage getMessageFromSources(String sources) throws Exception { - return new MimeMessage(Session.getDefaultInstance(new Properties()), new SharedByteArrayInputStream(sources.getBytes())); - } - -} http://git-wip-us.apache.org/repos/asf/james-project/blob/936746b9/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromStreamTest.java ---------------------------------------------------------------------- diff --git a/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromStreamTest.java b/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromStreamTest.java deleted file mode 100644 index e718dd8..0000000 --- a/server/container/core/src/test/java/org/apache/james/core/MimeMessageFromStreamTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************** - * 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.core; - -import javax.mail.Session; -import javax.mail.internet.MimeMessage; - -import java.io.ByteArrayInputStream; -import java.util.Properties; - -public class MimeMessageFromStreamTest extends MimeMessageTest { - - protected MimeMessage getMessageFromSources(String sources) throws Exception { - return new MimeMessage(Session.getDefaultInstance(new Properties()), new ByteArrayInputStream(sources.getBytes())); - } - - @Override - protected MimeMessage getMultipartMessage() throws Exception { - return getMessageFromSources(getMultipartMessageSource()); - } - - @Override - protected MimeMessage getSimpleMessage() throws Exception { - return getMessageFromSources(getSimpleMessageCleanedSource()); - } - - @Override - protected MimeMessage getMessageWithBadReturnPath() throws Exception { - return getMessageFromSources(getMessageWithBadReturnPathSource()); - } - - @Override - protected MimeMessage getMissingEncodingAddHeaderMessage() throws Exception { - return getMessageFromSources(getMissingEncodingAddHeaderSource()); - } - - @Override - protected MimeMessage getMissingEncodingMessage() throws Exception { - return getMessageFromSources(getMissingEncodingMessageSource()); - } - -} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org