2
false
+ false
false
diff --git a/api/src/main/java/jakarta/mail/BodyPart.java b/api/src/main/java/jakarta/mail/BodyPart.java
index a9eec9c18..ce83846a6 100644
--- a/api/src/main/java/jakarta/mail/BodyPart.java
+++ b/api/src/main/java/jakarta/mail/BodyPart.java
@@ -16,6 +16,8 @@
package jakarta.mail;
+import jakarta.mail.util.StreamProvider;
+
/**
* This class models a Part that is contained within a Multipart.
* This is an abstract class. Subclasses provide actual implementations.
@@ -36,6 +38,13 @@ public abstract class BodyPart implements Part {
*/
protected Multipart parent;
+ /**
+ * Instance of stream provider.
+ *
+ * @since JavaMail 2.1
+ */
+ protected final StreamProvider streamProvider = StreamProvider.provider();
+
/**
* Creates a default {@code BodyPart}.
*/
diff --git a/api/src/main/java/jakarta/mail/Multipart.java b/api/src/main/java/jakarta/mail/Multipart.java
index 8ba962c8d..1ca028c63 100644
--- a/api/src/main/java/jakarta/mail/Multipart.java
+++ b/api/src/main/java/jakarta/mail/Multipart.java
@@ -21,6 +21,7 @@
import java.io.OutputStream;
import java.io.IOException;
import jakarta.activation.DataSource;
+import jakarta.mail.util.StreamProvider;
/**
* Multipart is a container that holds multiple body parts. Multipart
@@ -60,6 +61,13 @@ public abstract class Multipart {
*/
protected Part parent;
+ /**
+ * Instance of stream provider.
+ *
+ * @since JavaMail 2.1
+ */
+ protected final StreamProvider streamProvider = StreamProvider.provider();
+
/**
* Default constructor. An empty Multipart object is created.
*/
diff --git a/api/src/main/java/jakarta/mail/Session.java b/api/src/main/java/jakarta/mail/Session.java
index 3382dd237..5fe4d54b5 100644
--- a/api/src/main/java/jakarta/mail/Session.java
+++ b/api/src/main/java/jakarta/mail/Session.java
@@ -35,7 +35,6 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -203,8 +202,8 @@ public final class Session {
// Support legacy @DefaultProvider
private static final String DEFAULT_PROVIDER = "com.sun.mail.util.DefaultProvider";
- public static final StreamProvider STREAM_PROVIDER;
+ private final StreamProvider streamProvider;
private final Properties props;
private final Authenticator authenticator;
private final Hashtable authTable
@@ -246,13 +245,13 @@ public String run() {
// ignore any exceptions
}
confDir = dir;
- STREAM_PROVIDER = getStreamProvider();
}
// Constructor is not public
private Session(Properties props, Authenticator authenticator) {
this.props = props;
this.authenticator = authenticator;
+ this.streamProvider = StreamProvider.provider();
if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue())
debug = true;
@@ -272,14 +271,15 @@ private Session(Properties props, Authenticator authenticator) {
q = new EventQueue((Executor)props.get("mail.event.executor"));
}
- private static StreamProvider getStreamProvider() {
- ServiceLoader sl = ServiceLoader.load(StreamProvider.class);
- Iterator iter = sl.iterator();
- if (iter.hasNext()) {
- return iter.next();
- } else {
- throw new IllegalStateException("Not provider of " + StreamProvider.class.getName() + " was found");
- }
+ /**
+ * Get the stream provider instance of the session.
+ *
+ * @return the stream provider
+ *
+ * @since JavaMail 2.1
+ */
+ public StreamProvider getStreamProvider() {
+ return streamProvider;
}
private final synchronized void initLogger() {
@@ -1036,7 +1036,7 @@ public void load(InputStream is) throws IOException {
private void loadProvidersFromStream(InputStream is) throws IOException {
if (is != null) {
- LineInputStream lis = Session.STREAM_PROVIDER.inputLineStream(is, false);
+ LineInputStream lis = streamProvider.inputLineStream(is, false);
String currLine;
// load and process one line at a time using LineInputStream
diff --git a/api/src/main/java/jakarta/mail/internet/InternetHeaders.java b/api/src/main/java/jakarta/mail/internet/InternetHeaders.java
index 32e4f6b72..fb5b94840 100644
--- a/api/src/main/java/jakarta/mail/internet/InternetHeaders.java
+++ b/api/src/main/java/jakarta/mail/internet/InternetHeaders.java
@@ -16,22 +16,19 @@
package jakarta.mail.internet;
-import jakarta.mail.Header;
-import jakarta.mail.MessagingException;
-import jakarta.mail.Session;
-import jakarta.mail.util.LineInputStream;
-import jakarta.mail.util.StreamProvider;
-
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.NoSuchElementException;
+import jakarta.mail.Header;
+import jakarta.mail.MessagingException;
+import jakarta.mail.util.LineInputStream;
+import jakarta.mail.util.StreamProvider;
+
/**
@@ -407,7 +404,7 @@ public void load(InputStream is, boolean allowutf8)
// Read header lines until a blank line. It is valid
// to have BodyParts with no header lines.
String line;
- LineInputStream lis = Session.STREAM_PROVIDER.inputLineStream(is, allowutf8);
+ LineInputStream lis = StreamProvider.provider().inputLineStream(is, allowutf8);
String prevline = null; // the previous header line, as a string
// a buffer to accumulate the header in, when we know it's needed
StringBuilder lineBuffer = new StringBuilder();
diff --git a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java
index 8a4a955ff..567126129 100644
--- a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java
+++ b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java
@@ -1693,7 +1693,7 @@ static void writeTo(MimePart part, OutputStream os, String[] ignoreList, boolean
} else {
Map params = new HashMap<>();
params.put("allowutf8", allowutf8);
- los = Session.STREAM_PROVIDER.outputLineStream(os, allowutf8);
+ los = StreamProvider.provider().outputLineStream(os, allowutf8);
}
// First, write out the header
diff --git a/api/src/main/java/jakarta/mail/internet/MimeMessage.java b/api/src/main/java/jakarta/mail/internet/MimeMessage.java
index 22bc03888..93842bb7f 100644
--- a/api/src/main/java/jakarta/mail/internet/MimeMessage.java
+++ b/api/src/main/java/jakarta/mail/internet/MimeMessage.java
@@ -258,7 +258,7 @@ public MimeMessage(MimeMessage source) throws MessagingException {
strict = source.strict;
source.writeTo(bos);
bos.close();
- InputStream bis = Session.STREAM_PROVIDER.inputSharedByteArray(bos.toByteArray());
+ InputStream bis = session.getStreamProvider().inputSharedByteArray(bos.toByteArray());
parse(bis);
bis.close();
saved = true;
@@ -1425,7 +1425,7 @@ protected InputStream getContentStream() throws MessagingException {
if (contentStream != null)
return ((SharedInputStream)contentStream).newStream(0, -1);
if (content != null) {
- return Session.STREAM_PROVIDER.inputSharedByteArray(content);
+ return session.getStreamProvider().inputSharedByteArray(content);
}
throw new MessagingException("No MimeMessage content");
}
@@ -1932,7 +1932,7 @@ public void writeTo(OutputStream os, String[] ignoreList)
// Else, the content is untouched, so we can just output it
// First, write out the header
Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList);
- LineOutputStream los = Session.STREAM_PROVIDER.outputLineStream(os, allowutf8Headers);
+ LineOutputStream los = session.getStreamProvider().outputLineStream(os, allowutf8Headers);
while (hdrLines.hasMoreElements())
los.writeln(hdrLines.nextElement());
diff --git a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java b/api/src/main/java/jakarta/mail/internet/MimeMultipart.java
index 48ae3e01a..a602983ca 100644
--- a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java
+++ b/api/src/main/java/jakarta/mail/internet/MimeMultipart.java
@@ -16,6 +16,14 @@
package jakarta.mail.internet;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
import jakarta.activation.DataSource;
import jakarta.mail.BodyPart;
import jakarta.mail.IllegalWriteException;
@@ -25,18 +33,8 @@
import jakarta.mail.Multipart;
import jakarta.mail.MultipartDataSource;
import jakarta.mail.Session;
-import jakarta.mail.internet.MimeUtility;
import jakarta.mail.util.LineInputStream;
import jakarta.mail.util.LineOutputStream;
-import jakarta.mail.util.StreamProvider;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
@@ -524,7 +522,7 @@ public synchronized void writeTo(OutputStream os)
String boundary = "--" +
(new ContentType(contentType)).getParameter("boundary");
- LineOutputStream los = Session.STREAM_PROVIDER.outputLineStream(os, false);
+ LineOutputStream los = streamProvider.outputLineStream(os, false);
// if there's a preamble, write it out
if (preamble != null) {
byte[] pb = MimeUtility.getBytes(preamble);
@@ -605,7 +603,7 @@ protected synchronized void parse() throws MessagingException {
try {
// Skip and save the preamble
- LineInputStream lin = Session.STREAM_PROVIDER.inputLineStream(in, false);
+ LineInputStream lin = streamProvider.inputLineStream(in, false);
StringBuilder preamblesb = null;
String line;
while ((line = lin.readLine()) != null) {
diff --git a/api/src/main/java/jakarta/mail/internet/MimeUtility.java b/api/src/main/java/jakarta/mail/internet/MimeUtility.java
index 890f465fd..43fae2019 100644
--- a/api/src/main/java/jakarta/mail/internet/MimeUtility.java
+++ b/api/src/main/java/jakarta/mail/internet/MimeUtility.java
@@ -22,7 +22,6 @@
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
@@ -38,8 +37,8 @@
import jakarta.activation.DataSource;
import jakarta.mail.EncodingAware;
import jakarta.mail.MessagingException;
-import jakarta.mail.Session;
import jakarta.mail.util.LineInputStream;
+import jakarta.mail.util.StreamProvider;
import jakarta.mail.util.StreamProvider.EncoderTypes;
/**
@@ -366,17 +365,17 @@ public static String getEncoding(DataHandler dh) {
public static InputStream decode(InputStream is, String encoding)
throws MessagingException {
if (encoding.equalsIgnoreCase(EncoderTypes.BASE_64.getEncoder()))
- return Session.STREAM_PROVIDER.inputBase64(is);
+ return StreamProvider.provider().inputBase64(is);
else if (encoding.equalsIgnoreCase(EncoderTypes.QUOTED_PRINTABLE_ENCODER.getEncoder()))
- return Session.STREAM_PROVIDER.inputQP(is);
+ return StreamProvider.provider().inputQP(is);
else if (encoding.equalsIgnoreCase(EncoderTypes.UU_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.X_UU_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.X_UUE.getEncoder()))
- return Session.STREAM_PROVIDER.inputUU(is);
+ return StreamProvider.provider().inputUU(is);
else if (encoding.equalsIgnoreCase(EncoderTypes.BINARY_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.BIT7_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.BIT8_ENCODER.getEncoder()))
- return Session.STREAM_PROVIDER.inputBinary(is);
+ return StreamProvider.provider().inputBinary(is);
else {
if (!ignoreUnknownEncoding)
throw new MessagingException("Unknown encoding: " + encoding);
@@ -401,17 +400,17 @@ public static OutputStream encode(OutputStream os, String encoding)
if (encoding == null)
return os;
else if (encoding.equalsIgnoreCase(EncoderTypes.BASE_64.getEncoder()))
- return Session.STREAM_PROVIDER.outputBase64(os);
+ return StreamProvider.provider().outputBase64(os);
else if (encoding.equalsIgnoreCase(EncoderTypes.QUOTED_PRINTABLE_ENCODER.getEncoder()))
- return Session.STREAM_PROVIDER.outputQP(os);
+ return StreamProvider.provider().outputQP(os);
else if (encoding.equalsIgnoreCase(EncoderTypes.UU_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.X_UU_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.X_UUE.getEncoder()))
- return Session.STREAM_PROVIDER.outputUU(os, null);
+ return StreamProvider.provider().outputUU(os, null);
else if (encoding.equalsIgnoreCase(EncoderTypes.BINARY_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.BIT7_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.BIT8_ENCODER.getEncoder()))
- return Session.STREAM_PROVIDER.outputBinary(os);
+ return StreamProvider.provider().outputBinary(os);
else
throw new MessagingException("Unknown encoding: " +encoding);
}
@@ -439,17 +438,17 @@ public static OutputStream encode(OutputStream os, String encoding,
if (encoding == null)
return os;
else if (encoding.equalsIgnoreCase(EncoderTypes.BASE_64.getEncoder()))
- return Session.STREAM_PROVIDER.outputBase64(os);
+ return StreamProvider.provider().outputBase64(os);
else if (encoding.equalsIgnoreCase(EncoderTypes.QUOTED_PRINTABLE_ENCODER.getEncoder()))
- return Session.STREAM_PROVIDER.outputQP(os);
+ return StreamProvider.provider().outputQP(os);
else if (encoding.equalsIgnoreCase(EncoderTypes.UU_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.X_UU_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.X_UUE.getEncoder()))
- return Session.STREAM_PROVIDER.outputUU(os, filename);
+ return StreamProvider.provider().outputUU(os, filename);
else if (encoding.equalsIgnoreCase(EncoderTypes.BINARY_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.BIT7_ENCODER.getEncoder()) ||
encoding.equalsIgnoreCase(EncoderTypes.BIT8_ENCODER.getEncoder()))
- return Session.STREAM_PROVIDER.outputBinary(os);
+ return StreamProvider.provider().outputBinary(os);
else
throw new MessagingException("Unknown encoding: " +encoding);
}
@@ -822,9 +821,9 @@ private static void doEncode(String string, boolean b64,
ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStream eos; // the encoder
if (b64) { // "B" encoding
- eos = Session.STREAM_PROVIDER.outputB(os);
+ eos = StreamProvider.provider().outputB(os);
} else { // "Q" encoding
- eos = Session.STREAM_PROVIDER.outputQ(os, encodingWord);
+ eos = StreamProvider.provider().outputQ(os, encodingWord);
}
try { // do the encoding
@@ -911,9 +910,9 @@ public static String decodeWord(String eword)
// Get the appropriate decoder
InputStream is;
if (encoding.equalsIgnoreCase("B"))
- is = Session.STREAM_PROVIDER.inputBase64(bis);
+ is = StreamProvider.provider().inputBase64(bis);
else if (encoding.equalsIgnoreCase("Q"))
- is = Session.STREAM_PROVIDER.inputQ(bis);
+ is = StreamProvider.provider().inputQ(bis);
else
throw new UnsupportedEncodingException(
"unknown encoding: " + encoding);
@@ -1369,7 +1368,7 @@ static String getDefaultMIMECharset() {
if (is != null) {
try {
- LineInputStream lineInput = Session.STREAM_PROVIDER.inputLineStream(is, false);
+ LineInputStream lineInput = StreamProvider.provider().inputLineStream(is, false);
// Load the JDK-to-MIME charset mapping table
loadMappings(lineInput, java2mime);
diff --git a/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java b/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java
index da8d2703d..a6330453a 100644
--- a/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java
+++ b/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java
@@ -16,15 +16,13 @@
package jakarta.mail.internet;
-import jakarta.mail.MessagingException;
-import jakarta.mail.Session;
-import jakarta.mail.util.LineOutputStream;
-import jakarta.mail.util.StreamProvider;
-
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
+import jakarta.mail.MessagingException;
+import jakarta.mail.util.LineOutputStream;
+
/**
* A MimeBodyPart that handles data that has already been encoded.
* This class is useful when constructing a message and attaching
@@ -80,7 +78,7 @@ public void writeTo(OutputStream os)
if (os instanceof LineOutputStream) {
los = (LineOutputStream) os;
} else {
- los = Session.STREAM_PROVIDER.outputLineStream(os, false);
+ los = streamProvider.outputLineStream(os, false);
}
// First, write out the header
diff --git a/api/src/main/java/jakarta/mail/util/FactoryFinder.java b/api/src/main/java/jakarta/mail/util/FactoryFinder.java
new file mode 100644
index 000000000..7b90facdb
--- /dev/null
+++ b/api/src/main/java/jakarta/mail/util/FactoryFinder.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.mail.util;
+
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+class FactoryFinder {
+
+ /**
+ * Finds the implementation {@code Class} object for the given
+ * factory type.
+ * The arguments supplied must be used in order
+ * This method is package private so that this code can be shared.
+ *
+ * @return the {@code Class} object of the specified message factory
+ *
+ * @param factoryClass factory abstract class or interface to be found
+ * @exception RuntimeException if there is an error
+ */
+ static T find(Class factoryClass) throws RuntimeException {
+
+ String factoryId = factoryClass.getName();
+
+ // Use the system property first
+ String className = fromSystemProperty(factoryId);
+ if (className != null) {
+ T result = newInstance(className);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ // standard services: java.util.ServiceLoader
+ T factory = factoryFromServiceLoader(factoryClass);
+ if (factory != null) {
+ return factory;
+ }
+
+ // handling Glassfish/OSGi (platform specific default)
+ if (isOsgi()) {
+ T result = lookupUsingOSGiServiceLoader(factoryId);
+ if (result != null) {
+ return result;
+ }
+ }
+ throw new IllegalStateException("Not provider of " + factoryClass.getName() + " was found");
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static T newInstance(String className) throws RuntimeException {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ checkPackageAccess(className);
+ Class clazz = null;
+ try {
+ if (classLoader == null) {
+ clazz = (Class) Class.forName(className);
+ } else {
+ clazz = (Class) classLoader.loadClass(className);
+ }
+ return clazz.getConstructor().newInstance();
+ } catch (ReflectiveOperationException e) {
+ throw new IllegalArgumentException("Cannot instance " + className, e);
+ }
+ }
+
+ private static String fromSystemProperty(String factoryId) {
+ String systemProp = getSystemProperty(factoryId);
+ return systemProp;
+ }
+
+ private static String getSystemProperty(final String property) {
+ String value = AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public String run() {
+ return System.getProperty(property);
+ }
+ });
+ return value;
+ }
+
+ private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader";
+
+ private static boolean isOsgi() {
+ try {
+ Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
+ return true;
+ } catch (ClassNotFoundException ignored) {
+ }
+ return false;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static T lookupUsingOSGiServiceLoader(String factoryId) {
+ try {
+ // Use reflection to avoid having any dependency on HK2 ServiceLoader class
+ Class> serviceClass = Class.forName(factoryId);
+ Class>[] args = new Class>[]{serviceClass};
+ Class> target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
+ Method m = target.getMethod("lookupProviderInstances", Class.class);
+ Iterator> iter = ((Iterable>) m.invoke(null, (Object[]) args)).iterator();
+ return iter.hasNext() ? (T) iter.next() : null;
+ } catch (Exception ignored) {
+ // log and continue
+ return null;
+ }
+ }
+
+ private static T factoryFromServiceLoader(Class factory) {
+ try {
+ ServiceLoader sl = ServiceLoader.load(factory);
+ Iterator iter = sl.iterator();
+ if (iter.hasNext()) {
+ return iter.next();
+ } else {
+ return null;
+ }
+ } catch (Throwable t) {
+ // For example, ServiceConfigurationError can be thrown if the factory class is not declared with 'uses' in module-info
+ throw new IllegalStateException("Cannot load " + factory + " as ServiceLoader", t);
+ }
+ }
+
+ private static void checkPackageAccess(String className) {
+ // make sure that the current thread has an access to the package of the given name.
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ int i = className.lastIndexOf('.');
+ if (i != -1) {
+ s.checkPackageAccess(className.substring(0, i));
+ }
+ }
+ }
+}
+
diff --git a/api/src/main/java/jakarta/mail/util/StreamProvider.java b/api/src/main/java/jakarta/mail/util/StreamProvider.java
index 078cd8e85..274168a2f 100644
--- a/api/src/main/java/jakarta/mail/util/StreamProvider.java
+++ b/api/src/main/java/jakarta/mail/util/StreamProvider.java
@@ -18,6 +18,8 @@
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.ServiceLoader;
/**
* Service lookup is used to find implementations of this interface.
@@ -25,11 +27,14 @@
* It contains the methods to instance different encoders/decoders and
* other streams required by the API.
*
+ * @since JavaMail 2.1
*/
public interface StreamProvider {
/**
* Enumeration with the different encoder types supported by the Mail API.
+ *
+ * @since JavaMail 2.1
*/
public static enum EncoderTypes {
@@ -159,4 +164,16 @@ public String getEncoder() {
* @return the encoder
*/
OutputStream outputUU(OutputStream out, String filename);
+
+ /**
+ * Creates a stream provider object. The provider is loaded using the
+ * {@link ServiceLoader#load(Class)} method. If there are no available
+ * service providers, this method throws an IllegalStateException.
+ * Users are recommended to cache the result of this method.
+ *
+ * @return a stream provider
+ */
+ public static StreamProvider provider() {
+ return FactoryFinder.find(StreamProvider.class);
+ }
}
diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java
index c46f191eb..8fdf526f1 100644
--- a/api/src/main/java/module-info.java
+++ b/api/src/main/java/module-info.java
@@ -27,4 +27,6 @@
uses jakarta.mail.Provider;
uses jakarta.mail.util.StreamProvider;
+ //reflective call to java.beans.Beans.instantiate
+ requires static java.desktop;
}
diff --git a/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java b/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java
new file mode 100644
index 000000000..0bdb117dc
--- /dev/null
+++ b/api/src/test/java/jakarta/mail/util/FactoryFinderTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package jakarta.mail.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.util.ServiceConfigurationError;
+
+import org.junit.Test;
+
+public class FactoryFinderTest {
+
+ @Test
+ public void specifiedInSystemProperty() {
+ System.setProperty(Class1.class.getName(), Class2.class.getName());
+ Class1 impl = FactoryFinder.find(Class1.class);
+ assertEquals(Class2.class, impl.getClass());
+ }
+
+ @Test
+ public void specifiedInServiceLoader() {
+ StreamProvider impl = FactoryFinder.find(StreamProvider.class);
+ assertEquals(DummyStreamProvider.class, impl.getClass());
+ }
+
+ @Test
+ public void doesNotExist() {
+ try {
+ FactoryFinder.find(Class2.class);
+ fail("IllegalStateException is expected");
+ } catch (IllegalStateException e) {
+ assertNull(e.getCause());
+ }
+ try {
+ FactoryFinder.find(Class3.class);
+ fail("IllegalStateException is expected");
+ } catch (IllegalStateException e) {
+ assertEquals("Not provider of " + Class3.class.getName() + " was found", e.getMessage());
+ }
+ }
+
+ public static class Class1 {}
+ public static class Class2 extends Class1 {}
+ public static class Class3 {}
+}
diff --git a/api/src/test/java/module-info.java b/api/src/test/java/module-info.java
index 610496d6e..e3ca4441a 100644
--- a/api/src/test/java/module-info.java
+++ b/api/src/test/java/module-info.java
@@ -20,6 +20,14 @@
requires transitive jakarta.activation;
requires junit;
+ exports jakarta.mail;
+ exports jakarta.mail.event;
+ exports jakarta.mail.internet;
+ exports jakarta.mail.search;
+ exports jakarta.mail.util;
+
uses jakarta.mail.Provider;
uses jakarta.mail.util.StreamProvider;
-}
+ uses jakarta.mail.util.FactoryFinderTest.Class2;
+ provides jakarta.mail.util.StreamProvider with jakarta.mail.util.DummyStreamProvider;
+}
\ No newline at end of file
diff --git a/docker/build_jakartamail.sh b/docker/build_jakartamail.sh
index 41fcba601..4af8803d6 100755
--- a/docker/build_jakartamail.sh
+++ b/docker/build_jakartamail.sh
@@ -1,6 +1,6 @@
#!/bin/bash -xe
#
-# Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0, which is available at
@@ -15,4 +15,6 @@
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
# Build
+
+cd ${WORKSPACE}/api
mvn -B -V -Pstaging clean install
diff --git a/docker/run_jakartamailtck.sh b/docker/run_jakartamailtck.sh
index 3c659f96a..28594b38c 100755
--- a/docker/run_jakartamailtck.sh
+++ b/docker/run_jakartamailtck.sh
@@ -1,6 +1,6 @@
#!/bin/bash -xe
#
-# Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,14 +16,23 @@
WGET_PROPS="-q --no-cache"
if [ -z "$JAF_BUNDLE_URL" ];then
- export JAF_BUNDLE_URL=https://repo1.maven.org/maven2/com/sun/activation/jakarta.activation/2.0.0-RC3/jakarta.activation-2.0.0-RC3.jar
+ export JAF_BUNDLE_URL=https://repo1.maven.org/maven2/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar
+fi
+if [ -z "$ANGUS_JAF_BUNDLE_URL" ];then
+ export ANGUS_JAF_BUNDLE_URL=https://repo1.maven.org/maven2/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar
fi
if [ -z "$MAIL_TCK_BUNDLE_URL" ];then
- export MAIL_TCK_BUNDLE_URL=https://ci.eclipse.org/mail/job/mail-tck/job/2.0.0/lastSuccessfulBuild/artifact/bundles/mail-tck-2.0.0-rc1.zip
+ export MAIL_TCK_BUNDLE_URL=https://ci.eclipse.org/mail/job/mail-tck/job/master/lastSuccessfulBuild/artifact/bundles/jakarta-mail-tck-2.0.1.zip
+fi
+if [ -z "$ANGUS_MAIL_BUNDLE_URL" ];then
+ export ANGUS_MAIL_BUNDLE_URL=https://repo1.maven.org/maven2/org/eclipse/angus/angus-mail/1.0.0/angus-mail-1.0.0.jar
fi
-wget $WGET_PROPS $JAF_BUNDLE_URL -O jakarta.activation.jar
-wget $WGET_PROPS $MAIL_TCK_BUNDLE_URL -O mailtck.zip
-cp ${WORKSPACE}/mail/target/jakarta.mail.jar ${WORKSPACE}
+wget $WGET_PROPS $JAF_BUNDLE_URL -O jakarta.activation-api.jar
+wget $WGET_PROPS $ANGUS_JAF_BUNDLE_URL -O angus-activation.jar
+# There is no latest mail-tck bundle in Jenkins yet. It will use the bundle of ${WORKSPACE}/mailtck.zip.
+#wget $WGET_PROPS $MAIL_TCK_BUNDLE_URL -O mailtck.zip
+wget $WGET_PROPS $ANGUS_MAIL_BUNDLE_URL -O angus-mail.jar
+cp ${WORKSPACE}/api/target/jakarta.mail-api-*.jar ${WORKSPACE}/jakarta.mail-api.jar
unzip -q -o ${WORKSPACE}/mailtck.zip -d ${WORKSPACE}
@@ -42,10 +51,28 @@ sed -i "s#^SMTP_DOMAIN=.*#SMTP_DOMAIN=james.local#g" "$TS_HOME/lib/ts.jte"
sed -i "s#^SMTP_FROM=.*#SMTP_FROM=user01@james.local#g" "$TS_HOME/lib/ts.jte"
sed -i "s#^SMTP_TO=.*#SMTP_TO=user01@james.local#g" "$TS_HOME/lib/ts.jte"
+
+sed -i "s#^TS_HOME=.*#TS_HOME=$TS_HOME#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVA_HOME=.*#JAVA_HOME=$JAVA_HOME#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JARPATH=.*#JARPATH=$WORKSPACE#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVAMAIL_SERVER=.*#JAVAMAIL_SERVER=localhost -pn 1143#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVAMAIL_PROTOCOL=.*#JAVAMAIL_PROTOCOL=imap#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVAMAIL_TRANSPORT_PROTOCOL=.*#JAVAMAIL_TRANSPORT_PROTOCOL=smtp#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVAMAIL_TRANSPORT_SERVER=.*#JAVAMAIL_TRANSPORT_SERVER=localhost -tpn 1025#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVAMAIL_USERNAME=.*#JAVAMAIL_USERNAME=$MAIL_USER#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^JAVAMAIL_PASSWORD=.*#JAVAMAIL_PASSWORD=1234#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^SMTP_DOMAIN=.*#SMTP_DOMAIN=james.local#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^SMTP_FROM=.*#SMTP_FROM=user01@james.local#g" "$TS_HOME/lib/ts.pluggability.jte"
+sed -i "s#^SMTP_TO=.*#SMTP_TO=user01@james.local#g" "$TS_HOME/lib/ts.pluggability.jte"
+
mkdir -p ${HOME}/.m2
cd $TS_HOME/tests/mailboxes
-export CLASSPATH=$TS_HOME/tests/mailboxes:$WORKSPACE/jakarta.mail.jar:$WORKSPACE/jakarta.activation.jar:$CLASSPATH
+export CLASSPATH=$TS_HOME/tests/mailboxes:$WORKSPACE/jakarta.mail-api.jar:$WORKSPACE/angus-mail.jar:$WORKSPACE/jakarta.activation-api.jar:$WORKSPACE/angus-activation.jar:$CLASSPATH
+
+ls -lrta $WORKSPACE
+echo $CLASSPATH
+
javac -cp $CLASSPATH fpopulate.java
java -cp $CLASSPATH fpopulate -s test1 -d imap://user01%40james.local:1234@localhost:1143
@@ -53,13 +80,13 @@ which ant
ant -version
cd $WORKSPACE/mail-tck/
-ant -Dreport.dir=$WORKSPACE/JTreport/mail-tck -Dwork.dir=$WORKSPACE/JTwork/mail-tck run
+ant -Dreport.dir=$WORKSPACE/JTreport/mail-tck -Dwork.dir=$WORKSPACE/JTwork/mail-tck run run.pluggability
HOST=`hostname -f`
echo "1 mail-tck $HOST" > $WORKSPACE/args.txt
mkdir -p $WORKSPACE/results/junitreports/
-
+JT_REPORT_DIR=$WORKSPACE/JTreport
$JAVA_HOME/bin/java -Djunit.embed.sysout=true \
-jar ${WORKSPACE}/docker/JTReportParser/JTReportParser.jar \
$WORKSPACE/args.txt $WORKSPACE/JTreport $WORKSPACE/results/junitreports/
diff --git a/mailtck.zip b/mailtck.zip
new file mode 100755
index 000000000..28e4d8af2
Binary files /dev/null and b/mailtck.zip differ