Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move from MailDateFormat (not thread safe) to DateTimeFormatter #590 #713

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions api/src/main/java/jakarta/mail/internet/MimeMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ServiceConfigurationError;

Expand Down Expand Up @@ -173,7 +177,10 @@ public class MimeMessage extends Message implements MimePart {
protected Object cachedContent;

// Used to parse dates
private static final MailDateFormat mailDateFormat = new MailDateFormat();
static final DateTimeFormatter mailDateFormat = DateTimeFormatter
.ofPattern("EEE, d MMM yyyy HH:mm:ss Z (z)")
.withLocale(Locale.US)
.withZone(ZoneOffset.systemDefault());

// Should addresses in headers be parsed in "strict" mode?
private boolean strict = true;
Expand Down Expand Up @@ -546,10 +553,10 @@ protected Object readResolve() throws ObjectStreamException {
* between the type and the corresponding RFC 822 header is
* as follows:
* <pre>
* Message.RecipientType.TO "To"
* Message.RecipientType.CC "Cc"
* Message.RecipientType.BCC "Bcc"
* MimeMessage.RecipientType.NEWSGROUPS "Newsgroups"
* Message.RecipientType.TO "To"
* Message.RecipientType.CC "Cc"
* Message.RecipientType.BCC "Bcc"
* MimeMessage.RecipientType.NEWSGROUPS "Newsgroups"
* </pre><br>
*
* Returns null if the header specified by the type is not found
Expand Down Expand Up @@ -902,10 +909,8 @@ public Date getSentDate() throws MessagingException {
String s = getHeader("Date", null);
if (s != null) {
try {
synchronized (mailDateFormat) {
return mailDateFormat.parse(s);
}
} catch (ParseException pex) {
return Date.from(Instant.from(mailDateFormat.parse(s)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MailDateFormat has a concept of isLenient and per the API documents it was not clear to me if the default is lenient or strict. I wonder if you need to use withResolverStyle to set that behavior and how compatible with existing behavior, mainly with parsing of dates.

Looking at the MailDateFormatTest there are some tests that seem to imply leniency with lots of exceptions:

  1. lenientMustBeBackwardCompatible()
  2. mustUseLeniencyForParsing()
  3. mustFailOrSkipCfws()
  4. lenientMustAcceptNonMilitaryZoneNames()

I would think that for testing this version of the patch you would need to port the MailDateFormatTest into the MimeMessageTest. Which seems like a lot of work.

What are your thoughts on just getting rid of the static field and the synchronized block and just creating some method local garbage by just creating a new MailDateFormat for parsing and formatting?

} catch (RuntimeException e) {
return null;
}
}
Expand All @@ -930,9 +935,7 @@ public void setSentDate(Date d) throws MessagingException {
if (d == null)
removeHeader("Date");
else {
synchronized (mailDateFormat) {
setHeader("Date", mailDateFormat.format(d));
}
setHeader("Date", mailDateFormat.format(d.toInstant()));
}
}

Expand Down Expand Up @@ -1455,10 +1458,10 @@ public InputStream getRawInputStream() throws MessagingException {
* class MimePartDataSource implements DataSource {
* public getInputStream() {
* return MimeUtility.decode(
* getContentStream(), getEncoding());
* getContentStream(), getEncoding());
* }
*
* .... &lt;other DataSource methods&gt;
* .... &lt;other DataSource methods&gt;
* }
* </pre></blockquote>
*
Expand Down
42 changes: 42 additions & 0 deletions api/src/test/java/jakarta/mail/internet/MimeMessageTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.internet;

import static org.junit.Assert.assertEquals;

import java.text.ParseException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import org.junit.Test;

public class MimeMessageTest {

@Test
public void dateTimeFormatter() throws ParseException {
MailDateFormat mailDateFormat1 = new MailDateFormat();
DateTimeFormatter mailDateFormat2 = MimeMessage.mailDateFormat;
Date date = new Date(1341100798000L);
String s1 = mailDateFormat1.format(date);
String s2 = mailDateFormat2.format(date.toInstant());
assertEquals(s1, s2);
Date d1 = mailDateFormat1.parse(s1);
Date d2 = Date.from(Instant.from(mailDateFormat2.parse(s1)));
assertEquals(d1, d2);
}
}