diff --git a/api/src/main/java/jakarta/mail/BodyPart.java b/api/src/main/java/jakarta/mail/BodyPart.java index 975a393e..876a2214 100644 --- a/api/src/main/java/jakarta/mail/BodyPart.java +++ b/api/src/main/java/jakarta/mail/BodyPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024 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 @@ -39,13 +39,6 @@ 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}. */ @@ -74,4 +67,14 @@ public Multipart getParent() { void setParent(Multipart parent) { this.parent = parent; } + + @Override + public StreamProvider getStreamProvider() throws MessagingException { + if (parent != null) { + return parent.getStreamProvider(); + } else { + return Part.super.getStreamProvider(); + } + } + } diff --git a/api/src/main/java/jakarta/mail/Message.java b/api/src/main/java/jakarta/mail/Message.java index aa6ec409..f6dd263c 100644 --- a/api/src/main/java/jakarta/mail/Message.java +++ b/api/src/main/java/jakarta/mail/Message.java @@ -708,32 +708,21 @@ public boolean match(SearchTerm term) throws MessagingException { return term.match(this); } - /** - * Obtains the {@link StreamProvider} from the session, if exists. - * Otherwise it obtains it from - * {@link Session#getDefaultInstance(java.util.Properties, Authenticator)}. - * - * @return the StreamProvider implementation from the session. - * @throws MessagingException if errors. - * - * @since JavaMail 2.2 - */ - protected StreamProvider provider() throws MessagingException { + @Override + public StreamProvider getStreamProvider() throws MessagingException { try { try { final Session s = this.session; if (s != null) { return s.getStreamProvider(); } else { - return Session.getDefaultInstance(System.getProperties(), - null).getStreamProvider(); + return Session.getDefaultInstance(System.getProperties(), null).getStreamProvider(); } } catch (ServiceConfigurationError sce) { throw new IllegalStateException(sce); } } catch (RuntimeException re) { - throw new MessagingException("Unable to get " - + StreamProvider.class.getName(), re); + throw new MessagingException("Unable to get " + StreamProvider.class.getName(), re); } } } diff --git a/api/src/main/java/jakarta/mail/Multipart.java b/api/src/main/java/jakarta/mail/Multipart.java index fc72a715..f119cbe3 100644 --- a/api/src/main/java/jakarta/mail/Multipart.java +++ b/api/src/main/java/jakarta/mail/Multipart.java @@ -21,8 +21,6 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Multipart is a container that holds multiple body parts. Multipart @@ -44,7 +42,6 @@ public abstract class Multipart { - private static final Logger LOGGER = Logger.getLogger(Multipart.class.getName()); /** * Vector of BodyPart objects. */ @@ -64,13 +61,6 @@ public abstract class Multipart { */ protected Part parent; - /** - * Instance of stream provider. - * - * @since JavaMail 2.2 - */ - private volatile StreamProvider streamProvider; - /** * Default constructor. An empty Multipart object is created. */ @@ -270,26 +260,23 @@ public synchronized void setParent(Part parent) { this.parent = parent; } - protected StreamProvider provider() { - if (streamProvider == null) { - synchronized (this) { - if (streamProvider == null) { - if (parent != null && parent instanceof Message) { - try { - streamProvider = ((Message)parent).provider(); - } catch (MessagingException e) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "Cannot reuse streamProvider", e); - } - } - } - } - if (streamProvider == null) { - streamProvider = StreamProvider.provider(); - } - } + /** + * Obtains the {@link StreamProvider} from the parent, if possible. + * Otherwise it obtains it from + * {@link Session#getDefaultInstance(java.util.Properties, Authenticator)}. + * + * @return the StreamProvider implementation from the session. + * @throws MessagingException if errors. + * + * @since JavaMail 2.2 + */ + protected StreamProvider getStreamProvider() throws MessagingException { + Part parent = this.parent; + if (parent != null) { + return parent.getStreamProvider(); + } else { + return Session.getDefaultInstance(System.getProperties(), null).getStreamProvider(); } - return streamProvider; } } diff --git a/api/src/main/java/jakarta/mail/Part.java b/api/src/main/java/jakarta/mail/Part.java index 9743ed1b..906f5f36 100644 --- a/api/src/main/java/jakarta/mail/Part.java +++ b/api/src/main/java/jakarta/mail/Part.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024 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 @@ -17,6 +17,7 @@ package jakarta.mail; import jakarta.activation.DataHandler; +import jakarta.mail.util.StreamProvider; import java.io.IOException; import java.io.InputStream; @@ -85,7 +86,7 @@ public interface Part { * of the content. The size is appropriate for display in a * user interface to give the user a rough idea of the size * of this part. - * + *StreamProvider getStreamProvider() throws MessagingException * @return size of content in bytes * @throws MessagingException for failures */ @@ -210,7 +211,7 @@ public interface Part { void setDescription(String description) throws MessagingException; /** - * Get the filename associated with this part, if possible. + * Get the filename associated with this part, if possible.StreamProvider getStreamProvider() throws MessagingException * Useful if this part represents an "attachment" that was * loaded from a file. The filename will usually be a simple * name, not including directory components. @@ -453,4 +454,17 @@ Enumeration
getMatchingHeaders(String[] header_names) */ Enumeration
getNonMatchingHeaders(String[] header_names) throws MessagingException; + + /** + * Obtains the {@link StreamProvider}. + * It defaults to {@link Session#getDefaultInstance(java.util.Properties, Authenticator)}. + * + * @return the StreamProvider. + * @throws MessagingException if errors. + * + * @since JavaMail 2.2 + */ + default StreamProvider getStreamProvider() throws MessagingException { + return Session.getDefaultInstance(System.getProperties(), null).getStreamProvider(); + } } diff --git a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java index 7c6c3a67..f77a5f3f 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java +++ b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024 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 @@ -30,7 +30,6 @@ import jakarta.mail.Multipart; import jakarta.mail.Part; import jakarta.mail.util.LineOutputStream; -import jakarta.mail.util.StreamProvider; import jakarta.mail.util.StreamProvider.EncoderTypes; import java.io.BufferedInputStream; @@ -1630,7 +1629,7 @@ static void invalidateContentHeaders(MimePart part) part.removeHeader("Content-Type"); part.removeHeader("Content-Transfer-Encoding"); } - + static void writeTo(MimePart part, OutputStream os, String[] ignoreList) throws IOException, MessagingException { @@ -1641,7 +1640,7 @@ static void writeTo(MimePart part, OutputStream os, String[] ignoreList) } else { Map params = new HashMap<>(); params.put("allowutf8", allowutf8); - los = StreamProvider.provider().outputLineStream(os, allowutf8); + los = part.getStreamProvider().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 73b7c4c7..b9ab9f50 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeMessage.java +++ b/api/src/main/java/jakarta/mail/internet/MimeMessage.java @@ -29,7 +29,6 @@ import jakarta.mail.Multipart; import jakarta.mail.Session; import jakarta.mail.util.LineOutputStream; -import jakarta.mail.util.StreamProvider; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -45,8 +44,6 @@ import java.util.Enumeration; import java.util.List; import java.util.Properties; -import java.util.ServiceConfigurationError; - /** * This class represents a MIME style email message. It implements @@ -245,7 +242,7 @@ public MimeMessage(MimeMessage source) throws MessagingException { strict = source.strict; source.writeTo(bos); bos.close(); - try (InputStream bis = provider().inputSharedByteArray(bos.toByteArray())) { + try (InputStream bis = getStreamProvider().inputSharedByteArray(bos.toByteArray())) { parse(bis); } saved = true; @@ -1410,7 +1407,7 @@ protected InputStream getContentStream() throws MessagingException { if (contentStream != null) return ((SharedInputStream) contentStream).newStream(0, -1); if (content != null) { - return provider().inputSharedByteArray(content); + return getStreamProvider().inputSharedByteArray(content); } throw new MessagingException("No MimeMessage content"); } @@ -1917,7 +1914,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 = provider().outputLineStream(os, allowutf8); + LineOutputStream los = getStreamProvider().outputLineStream(os, allowutf8); 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 4a555e6e..dee43113 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java +++ b/api/src/main/java/jakarta/mail/internet/MimeMultipart.java @@ -520,7 +520,7 @@ public synchronized void writeTo(OutputStream os) String boundary = "--" + (new ContentType(contentType)).getParameter("boundary"); - LineOutputStream los = provider().outputLineStream(os, false); + LineOutputStream los = getStreamProvider().outputLineStream(os, false); // if there's a preamble, write it out if (preamble != null) { byte[] pb = MimeUtility.getBytes(preamble); @@ -601,7 +601,7 @@ protected synchronized void parse() throws MessagingException { try { // Skip and save the preamble - LineInputStream lin = provider().inputLineStream(in, false); + LineInputStream lin = getStreamProvider().inputLineStream(in, false); StringBuilder preamblesb = null; String line; while ((line = lin.readLine()) != null) { diff --git a/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java b/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java index 48f663f8..19cdce4c 100644 --- a/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java +++ b/api/src/main/java/jakarta/mail/internet/PreencodedMimeBodyPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024 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 @@ -78,7 +78,7 @@ public void writeTo(OutputStream os) if (os instanceof LineOutputStream) { los = (LineOutputStream) os; } else { - los = streamProvider.outputLineStream(os, false); + los = getStreamProvider().outputLineStream(os, false); } // First, write out the header diff --git a/api/src/test/java/jakarta/mail/PartTest.java b/api/src/test/java/jakarta/mail/PartTest.java new file mode 100644 index 00000000..1613e23b --- /dev/null +++ b/api/src/test/java/jakarta/mail/PartTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024 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; + +import static org.junit.Assert.assertEquals; + +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; +import jakarta.mail.util.DummyStreamProvider; +import jakarta.mail.util.LineInputStream; +import jakarta.mail.util.LineOutputStream; +import jakarta.mail.util.StreamProvider; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Properties; + +import org.junit.Test; + +public class PartTest { + + // Sessions with different StreamProviders + private static final Session SESSION_DEFAULT = Session.getDefaultInstance(new Properties()); + private static final Session SESSION_1; + private static final Session SESSION_2; + + static { + SESSION_1 = Session.getDefaultInstance(new Properties()); + System.setProperty(StreamProvider.class.getName(), CustomStreamProvider.class.getName()); + SESSION_2 = Session.getInstance(new Properties()); + System.clearProperty(StreamProvider.class.getName()); + } + + @Test + public void streamProvidersChecker() { + assertEquals(DummyStreamProvider.class, SESSION_DEFAULT.getStreamProvider().getClass()); + assertEquals(DummyStreamProvider.class, SESSION_1.getStreamProvider().getClass()); + assertEquals(CustomStreamProvider.class, SESSION_2.getStreamProvider().getClass()); + assertEquals(SESSION_DEFAULT.getStreamProvider(), SESSION_1.getStreamProvider()); + } + + @Test + public void sameInstance() throws MessagingException { + MimeBodyPart body = new MimeBodyPart(); + MimeMultipart multiPart = new MimeMultipart(); + assertEquals(SESSION_DEFAULT.getStreamProvider(), body.getStreamProvider()); + assertEquals(SESSION_DEFAULT.getStreamProvider(), multiPart.getStreamProvider()); + } + + @Test + public void specifySession1() throws MessagingException { + Message message = new MimeMessage(SESSION_1); + Multipart multipart = new MimeMultipart(); + message.setContent(multipart); + assertEquals(SESSION_1.getStreamProvider(), multipart.getStreamProvider()); + } + + @Test + public void specifySession2() throws MessagingException { + Message message = new MimeMessage(SESSION_2); + Multipart multipart = new MimeMultipart(); + message.setContent(multipart); + assertEquals(SESSION_2.getStreamProvider(), multipart.getStreamProvider()); + } + + public static class CustomStreamProvider implements StreamProvider { + + @Override + public InputStream inputBase64(InputStream in) { + return null; + } + + @Override + public OutputStream outputBase64(OutputStream out) { + return null; + } + + @Override + public InputStream inputBinary(InputStream in) { + return null; + } + + @Override + public OutputStream outputBinary(OutputStream out) { + return null; + } + + @Override + public OutputStream outputB(OutputStream out) { + return null; + } + + @Override + public InputStream inputQ(InputStream in) { + return null; + } + + @Override + public OutputStream outputQ(OutputStream out, boolean encodingWord) { + return null; + } + + @Override + public LineInputStream inputLineStream(InputStream in, boolean allowutf8) { + return null; + } + + @Override + public LineOutputStream outputLineStream(OutputStream out, boolean allowutf8) { + return null; + } + + @Override + public InputStream inputQP(InputStream in) { + // TODO Auto-generated method stub + return null; + } + + @Override + public OutputStream outputQP(OutputStream out) { + return null; + } + + @Override + public InputStream inputSharedByteArray(byte[] buff) { + return null; + } + + @Override + public InputStream inputUU(InputStream in) { + return null; + } + + @Override + public OutputStream outputUU(OutputStream out, String filename) { + return null; + } + } +}