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 05d2713c..b12586e7 100644 --- a/api/src/main/java/jakarta/mail/Message.java +++ b/api/src/main/java/jakarta/mail/Message.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,11 +17,13 @@ package jakarta.mail; import jakarta.mail.search.SearchTerm; +import jakarta.mail.util.StreamProvider; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.Date; +import java.util.ServiceConfigurationError; /** * This class models an email message. This is an abstract class. @@ -705,4 +707,22 @@ protected void setExpunged(boolean expunged) { public boolean match(SearchTerm term) throws MessagingException { return term.match(this); } + + @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(); + } + } catch (ServiceConfigurationError sce) { + throw new IllegalStateException(sce); + } + } catch (RuntimeException re) { + throw new NoSuchProviderException("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 ef634454..5702c64a 100644 --- a/api/src/main/java/jakarta/mail/Multipart.java +++ b/api/src/main/java/jakarta/mail/Multipart.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 @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.util.ServiceConfigurationError; import java.util.Vector; /** @@ -61,13 +62,6 @@ 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. */ @@ -266,4 +260,32 @@ public synchronized Part getParent() { public synchronized void setParent(Part parent) { this.parent = parent; } + + /** + * 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 { + try { + try { + return Session.getDefaultInstance(System.getProperties(), null).getStreamProvider(); + } catch (ServiceConfigurationError sce) { + throw new IllegalStateException(sce); + } + } catch (RuntimeException re) { + throw new NoSuchProviderException("Unable to get " + StreamProvider.class.getName(), re); + } + } + } + } diff --git a/api/src/main/java/jakarta/mail/Part.java b/api/src/main/java/jakarta/mail/Part.java index 9743ed1b..68cb4680 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,11 +17,13 @@ package jakarta.mail; import jakarta.activation.DataHandler; +import jakarta.mail.util.StreamProvider; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; +import java.util.ServiceConfigurationError; /** * The Part interface is the common base interface for @@ -453,4 +455,25 @@ 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 { + try { + try { + return Session.getDefaultInstance(System.getProperties(), null).getStreamProvider(); + } catch (ServiceConfigurationError sce) { + throw new IllegalStateException(sce); + } + } catch (RuntimeException re) { + throw new NoSuchProviderException("Unable to get " + StreamProvider.class.getName(), re); + } + } } diff --git a/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java b/api/src/main/java/jakarta/mail/internet/MimeBodyPart.java index 7c6c3a67..b0fa7a50 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; @@ -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 e832b097..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()); @@ -2322,23 +2319,4 @@ protected MimeMessage createMimeMessage(Session session) throws MessagingException { return new MimeMessage(session); } - - private StreamProvider provider() throws MessagingException { - try { - try { - final Session s = this.session; - if (s != null) { - return s.getStreamProvider(); - } else { - 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); - } - } } diff --git a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java b/api/src/main/java/jakarta/mail/internet/MimeMultipart.java index 88a149a1..dee43113 100644 --- a/api/src/main/java/jakarta/mail/internet/MimeMultipart.java +++ b/api/src/main/java/jakarta/mail/internet/MimeMultipart.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 @@ -520,7 +520,7 @@ public synchronized void writeTo(OutputStream os) String boundary = "--" + (new ContentType(contentType)).getParameter("boundary"); - LineOutputStream los = streamProvider.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 = streamProvider.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..cce883ae --- /dev/null +++ b/api/src/test/java/jakarta/mail/PartTest.java @@ -0,0 +1,170 @@ +/* + * 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()); + } + + @Test + public void multipartBodyPart() throws Exception { + MimeMessage m = new MimeMessage(SESSION_2); + MimeMultipart mmp = new MimeMultipart("mixed"); + MimeBodyPart mbp = new MimeBodyPart(); + mbp.setDisposition(Part.INLINE); + mbp.setText("none"); + mmp.addBodyPart(mbp); + m.setContent(mmp); + + StreamProvider root = SESSION_2.getStreamProvider(); + assertEquals(root, m.getStreamProvider()); + assertEquals(root, mmp.getStreamProvider()); + assertEquals(root, mbp.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; + } + } +} diff --git a/doc/release/CHANGES.txt b/doc/release/CHANGES.txt index efaf7d43..089a457c 100644 --- a/doc/release/CHANGES.txt +++ b/doc/release/CHANGES.txt @@ -19,6 +19,10 @@ Bug IDs that start with "G" can be found in the GlassFish Issue Tracker Seven digit bug numbers are from the old Sun bug database, which is no longer available. + CHANGES IN THE 2.2.0 RELEASE + ---------------------------- +E 699 Multipart performs blocking call in every instantiation + CHANGES IN THE 2.1.3 RELEASE ---------------------------- E 631 Session.getService does not use proper classloader in OSGI environment