Updated to JakartaMail 1.6.7

pull/198/head
M66B 4 years ago
parent a9034efbfe
commit 1227cd0b4a

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at * terms of the Eclipse Public License v. 2.0, which is available at
@ -30,7 +30,6 @@ import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import com.sun.mail.util.PropUtil; import com.sun.mail.util.PropUtil;
@ -545,23 +544,63 @@ public class Protocol {
public SocketChannel getChannel() { public SocketChannel getChannel() {
SocketChannel ret = socket.getChannel(); SocketChannel ret = socket.getChannel();
if (ret != null) if (ret != null)
return ret; return ret;
// XXX - Android is broken and SSL wrapped sockets don't delegate
// the getChannel method to the wrapped Socket
if (socket instanceof SSLSocket) { if (socket instanceof SSLSocket) {
try { ret = Protocol.findSocketChannel(socket);
Field f = socket.getClass().getDeclaredField("socket");
f.setAccessible(true);
Socket s = (Socket)f.get(socket);
ret = s.getChannel();
} catch (Exception ex) {
// ignore anything that might go wrong
}
} }
return ret; return ret;
} }
/**
* Android is broken and SSL wrapped sockets don't delegate
* the getChannel method to the wrapped Socket.
*
* @param socket a non null socket
* @return the SocketChannel or null if not found
*/
private static SocketChannel findSocketChannel(Socket socket) {
//Search class hierarchy for field name socket regardless of modifier.
for (Class<?> k = socket.getClass(); k != Object.class; k = k.getSuperclass()) {
try {
Field f = k.getDeclaredField("socket");
f.setAccessible(true);
Socket s = (Socket) f.get(socket);
SocketChannel ret = s.getChannel();
if (ret != null) {
return ret;
}
} catch (Exception ignore) {
//ignore anything that might go wrong
}
}
//Search class hierarchy for fields that can hold a Socket
//or subclass regardless of modifier. Fields declared as super types of Socket
//will be ignored.
for (Class<?> k = socket.getClass(); k != Object.class; k = k.getSuperclass()) {
try {
for (Field f : k.getDeclaredFields()) {
if (Socket.class.isAssignableFrom(f.getType())) {
try {
f.setAccessible(true);
Socket s = (Socket) f.get(socket);
SocketChannel ret = s.getChannel();
if (ret != null) {
return ret;
}
} catch (Exception ignore) {
//ignore anything that might go wrong
}
}
}
} catch (Exception ignore) {
//ignore anything that might go wrong
}
}
return null;
}
/** /**
* Return the local SocketAddress (host and port) for this * Return the local SocketAddress (host and port) for this
* end of the connection. * end of the connection.

@ -630,6 +630,7 @@ public class IMAPProtocol extends Protocol {
authenticated = true; authenticated = true;
} }
/** /**
* The AUTHENTICATE command with AUTH=PLAIN authentication scheme. * The AUTHENTICATE command with AUTH=PLAIN authentication scheme.
* This is based heavly on the {@link #authlogin} method. * This is based heavly on the {@link #authlogin} method.

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at * terms of the Eclipse Public License v. 2.0, which is available at

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at * terms of the Eclipse Public License v. 2.0, which is available at
@ -23,7 +23,6 @@ import java.security.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import com.sun.mail.auth.Ntlm; import com.sun.mail.auth.Ntlm;
import com.sun.mail.util.ASCIIUtility; import com.sun.mail.util.ASCIIUtility;
import com.sun.mail.util.BASE64DecoderStream; import com.sun.mail.util.BASE64DecoderStream;
@ -44,7 +43,7 @@ class Response {
} }
/** /**
* This class provides a POP3 connection and implements * This class provides a POP3 connection and implements
* the POP3 protocol requests. * the POP3 protocol requests.
* *
* APOP support courtesy of "chamness". * APOP support courtesy of "chamness".
@ -76,7 +75,7 @@ class Protocol {
// sometimes the returned size isn't quite big enough // sometimes the returned size isn't quite big enough
private static final int SLOP = 128; private static final int SLOP = 128;
/** /**
* Open a connection to the POP3 server. * Open a connection to the POP3 server.
*/ */
Protocol(String host, int port, MailLogger logger, Protocol(String host, int port, MailLogger logger,
@ -393,7 +392,7 @@ class Protocol {
} }
/** /**
* Gets the APOP message digest. * Gets the APOP message digest.
* From RFC 1939: * From RFC 1939:
* *
* The 'digest' parameter is calculated by applying the MD5 * The 'digest' parameter is calculated by applying the MD5
@ -446,6 +445,25 @@ class Protocol {
return enabled; return enabled;
} }
/**
* Run authentication query based on command and initial response
*
* @param command - command passed to server
* @param ir - initial response, part of the query
* @throws IOException
*/
protected void runAuthenticationCommand(String command, String ir) throws IOException {
if (logger.isLoggable(Level.FINE)) {
logger.fine(command + " using one line authentication format");
}
if (ir != null) {
resp = simpleCommand(command + " " + (ir.length() == 0 ? "=" : ir));
} else {
resp = simpleCommand(command);
}
}
/** /**
* Start the authentication handshake by issuing the AUTH command. * Start the authentication handshake by issuing the AUTH command.
* Delegate to the doAuth method to do the mechanism-specific * Delegate to the doAuth method to do the mechanism-specific
@ -461,11 +479,8 @@ class Protocol {
logger.fine("AUTH " + mech + " command trace suppressed"); logger.fine("AUTH " + mech + " command trace suppressed");
suspendTracing(); suspendTracing();
} }
if (ir != null)
resp = simpleCommand("AUTH " + mech + " " + runAuthenticationCommand("AUTH " + mech, ir);
(ir.length() == 0 ? "=" : ir));
else
resp = simpleCommand("AUTH " + mech);
if (resp.cont) if (resp.cont)
doAuth(host, authzid, user, passwd); doAuth(host, authzid, user, passwd);
@ -681,6 +696,26 @@ class Protocol {
return ASCIIUtility.toString(b); return ASCIIUtility.toString(b);
} }
@Override
protected void runAuthenticationCommand(String command, String ir) throws IOException {
Boolean isTwoLineAuthenticationFormat = getBoolProp(
props,
prefix + ".auth.xoauth2.two.line.authentication.format");
if (isTwoLineAuthenticationFormat) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(command + " using two line authentication format");
}
resp = twoLinesCommand(
command,
(ir.length() == 0 ? "=" : ir)
);
} else {
super.runAuthenticationCommand(command, ir);
}
}
@Override @Override
void doAuth(String host, String authzid, String user, String passwd) void doAuth(String host, String authzid, String user, String passwd)
throws IOException { throws IOException {
@ -1120,6 +1155,29 @@ class Protocol {
return r; return r;
} }
/**
* Issue a two line POP3 command and return the response
* Refer to {@link #simpleCommand(String)} for a single line command
*
* @param firstCommand first command we want to pass to server e.g AUTH XOAUTH2
* @param secondCommand second command e.g Base64 encoded authorization string
* @return Response
* @throws IOException
*/
private Response twoLinesCommand(String firstCommand, String secondCommand) throws IOException {
String cmd = firstCommand + " " + secondCommand;
batchCommandStart(cmd);
simpleCommand(firstCommand);
batchCommandContinue(cmd);
Response r = simpleCommand(secondCommand);
batchCommandEnd();
return r;
}
/** /**
* Send the specified command. * Send the specified command.
*/ */

@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- <!--
Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at terms of the Eclipse Public License v. 2.0, which is available at
@ -390,6 +390,13 @@ setting this property to "false" or by setting the "mail.pop3.auth.mechanisms"
property to "XOAUTH2".</TD> property to "XOAUTH2".</TD>
</TR> </TR>
<TR>
<TD><A ID="mail.pop3.auth.xoauth2.two.line.authentication.format">mail.pop3.auth.xoauth2.two.line.authentication.format</A></TD>
<TD>boolean</TD>
<TD>If true, splits authentication command on two lines.
Default is false.</TD>
</TR>
<TR> <TR>
<TD><A ID="mail.pop3.socketFactory">mail.pop3.socketFactory</A></TD> <TD><A ID="mail.pop3.socketFactory">mail.pop3.socketFactory</A></TD>
<TD>SocketFactory</TD> <TD>SocketFactory</TD>

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at * terms of the Eclipse Public License v. 2.0, which is available at

@ -322,12 +322,12 @@ public class WriteTimeoutSocket extends Socket {
* @return the FileDescriptor object * @return the FileDescriptor object
*/ */
public FileDescriptor getFileDescriptor$() { public FileDescriptor getFileDescriptor$() {
//The loop handles issues with non-public classes between //The loop handles issues with non-public classes between
//java.net.Socket and the actual socket type held in this object. //java.net.Socket and the actual socket type held in this object.
//Must inspect java.net.Socket to ensure compatiblity with old behavior. //Must inspect java.net.Socket to ensure compatiblity with old behavior.
for (Class<?> k = socket.getClass(); k != Object.class; k = k.getSuperclass()) { for (Class<?> k = socket.getClass(); k != Object.class; k = k.getSuperclass()) {
try { try {
Method m = k.getDeclaredMethod("getFileDescriptor$"); Method m = k.getDeclaredMethod("getFileDescriptor$");
if (FileDescriptor.class.isAssignableFrom(m.getReturnType())) { if (FileDescriptor.class.isAssignableFrom(m.getReturnType())) {
//Skip setAccessible so non-public methods fail to invoke. //Skip setAccessible so non-public methods fail to invoke.
return (FileDescriptor) m.invoke(socket); return (FileDescriptor) m.invoke(socket);

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2019 Jason Mehrens. All rights reserved. * Copyright (c) 2013, 2021 Jason Mehrens. All rights reserved.
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at * terms of the Eclipse Public License v. 2.0, which is available at
@ -21,8 +21,9 @@ import java.util.logging.LogRecord;
/** /**
* A plain text formatter that can produce fixed width output. By default this * A plain text formatter that can produce fixed width output. By default this
* formatter will produce output no greater than 160 characters wide plus the * formatter will produce output no greater than 160 characters
* separator and newline characters. Only specified fields support an * (Unicode code points) wide plus the separator and newline characters. Only
* specified fields support an
* {@linkplain #toAlternate(java.lang.String) alternate} fixed width format. * {@linkplain #toAlternate(java.lang.String) alternate} fixed width format.
* <p> * <p>
* By default each <code>CompactFormatter</code> is initialized using the * By default each <code>CompactFormatter</code> is initialized using the
@ -33,8 +34,10 @@ import java.util.logging.LogRecord;
* used. * used.
* <ul> * <ul>
* <li>&lt;formatter-name&gt;.format - the {@linkplain java.util.Formatter * <li>&lt;formatter-name&gt;.format - the {@linkplain java.util.Formatter
* format} string used to transform the output. The format string can be * format} string used to transform the output. The arguments are explained
* used to fix the output size. (defaults to <code>%7$#.160s%n</code>)</li> * in detail in the {@linkplain #format(java.util.logging.LogRecord) format}
* documentation. The format string can be used to fix the output size.
* (defaults to <code>%7$#.160s%n</code>)</li>
* </ul> * </ul>
* *
* @author Jason Mehrens * @author Jason Mehrens
@ -112,10 +115,12 @@ public class CompactFormatter extends java.util.logging.Formatter {
* and a relevant stack trace element if available; otherwise, an empty * and a relevant stack trace element if available; otherwise, an empty
* string is used.</li> * string is used.</li>
* <li>{@code message|thrown} The message and the thrown properties joined * <li>{@code message|thrown} The message and the thrown properties joined
* as one parameter. This parameter supports * as one parameter. Width and precision are by Unicode code points. This
* parameter supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li> * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code thrown|message} The thrown and message properties joined as * <li>{@code thrown|message} The thrown and message properties joined as
* one parameter. This parameter supports * one parameter. Width and precision are by Unicode code points. This
* parameter supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li> * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code sequence} the * <li>{@code sequence} the
* {@linkplain LogRecord#getSequenceNumber() sequence number} if the given * {@linkplain LogRecord#getSequenceNumber() sequence number} if the given
@ -128,10 +133,12 @@ public class CompactFormatter extends java.util.logging.Formatter {
* {@linkplain #formatError(LogRecord) error message} without any stack * {@linkplain #formatError(LogRecord) error message} without any stack
* trace.</li> * trace.</li>
* <li>{@code message|error} The message and error properties joined as one * <li>{@code message|error} The message and error properties joined as one
* parameter. This parameter supports * parameter. Width and precision are by Unicode code points. This parameter
* supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li> * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code error|message} The error and message properties joined as one * <li>{@code error|message} The error and message properties joined as one
* parameter. This parameter supports * parameter. Width and precision are by Unicode code points. This parameter
* supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li> * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code backtrace} only the * <li>{@code backtrace} only the
* {@linkplain #formatBackTrace(LogRecord) stack trace} of the given * {@linkplain #formatBackTrace(LogRecord) stack trace} of the given
@ -148,17 +155,19 @@ public class CompactFormatter extends java.util.logging.Formatter {
* <ul> * <ul>
* <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.160s%n} * <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.160s%n}
* <p> * <p>
* This prints only 160 characters of the message|thrown ({@code 7$}) using * This prints only 160 characters (Unicode code points) of the
* the {@linkplain #toAlternate(java.lang.String) alternate} form. The * message|thrown ({@code 7$}) using the
* separator is not included as part of the total width. * {@linkplain #toAlternate(java.lang.String) alternate} form. The separator
* is not included as part of the total width.
* <pre> * <pre>
* Encoding failed.|NullPointerException: null String.getBytes(:913) * Encoding failed.|NullPointerException: null String.getBytes(:913)
* </pre> * </pre>
* *
* <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.20s%n} * <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.20s%n}
* <p> * <p>
* This prints only 20 characters of the message|thrown ({@code 7$}) using * This prints only 20 characters (Unicode code points) of the
* the {@linkplain #toAlternate(java.lang.String) alternate} form. This will * message|thrown ({@code 7$}) using the
* {@linkplain #toAlternate(java.lang.String) alternate} form. This will
* perform a weighted truncation of both the message and thrown properties * perform a weighted truncation of both the message and thrown properties
* of the log record. The separator is not included as part of the total * of the log record. The separator is not included as part of the total
* width. * width.
@ -179,8 +188,9 @@ public class CompactFormatter extends java.util.logging.Formatter {
* *
* <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n} * <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n}
* <p> * <p>
* This prints the log level ({@code 4$}) and only 160 characters of the * This prints the log level ({@code 4$}) and only 160 characters
* message|error ({@code 12$}) using the alternate form. * (Unicode code points) of the message|error ({@code 12$}) using the
* alternate form.
* <pre> * <pre>
* SEVERE: Unable to send notification.|SocketException: Permission denied: connect * SEVERE: Unable to send notification.|SocketException: Permission denied: connect
* </pre> * </pre>
@ -335,21 +345,22 @@ public class CompactFormatter extends java.util.logging.Formatter {
} }
/** /**
* Formats the thread id property of the given log record. By default this * Formats the thread id property of the given log record. Long thread ids
* is formatted as a {@code long} by an unsigned conversion. * are preferred if supported. Otherwise, the integer thread id is
* formatted as a {@code long} by an unsigned conversion.
* *
* @param record the record. * @param record the record.
* @return the formatted thread id as a number. * @return the formatted thread id as a number.
* @throws NullPointerException if the given record is null. * @throws NullPointerException if the given record is null.
* @since JavaMail 1.5.4 * @since JavaMail 1.5.4
*/ */
@SuppressWarnings("deprecation") //See JDK-8245302
public Number formatThreadID(final LogRecord record) { public Number formatThreadID(final LogRecord record) {
/** Long id = LogManagerProperties.getLongThreadID(record);
* Thread.getID is defined as long and LogRecord.getThreadID is defined if (id == null) {
* as int. Convert to unsigned as a means to better map the two types of id = (((long) record.getThreadID()) & 0xffffffffL);
* thread identifiers. }
*/ return id;
return (((long) record.getThreadID()) & 0xffffffffL);
} }
/** /**
@ -474,12 +485,7 @@ public class CompactFormatter extends java.util.logging.Formatter {
*/ */
private String formatStackTraceElement(final StackTraceElement s) { private String formatStackTraceElement(final StackTraceElement s) {
String v = simpleClassName(s.getClassName()); String v = simpleClassName(s.getClassName());
String result; String result = s.toString().replace(s.getClassName(), v);
if (v != null) {
result = s.toString().replace(s.getClassName(), v);
} else {
result = s.toString();
}
//If the class name contains the simple file name then remove file name. //If the class name contains the simple file name then remove file name.
v = simpleFileName(s.getFileName()); v = simpleFileName(s.getFileName());
@ -753,7 +759,7 @@ public class CompactFormatter extends java.util.logging.Formatter {
* @return true if null or spaces. * @return true if null or spaces.
*/ */
private static boolean isNullOrSpaces(final String s) { private static boolean isNullOrSpaces(final String s) {
return s == null || s.trim().length() == 0; return s == null || s.trim().isEmpty();
} }
/** /**
@ -799,41 +805,58 @@ public class CompactFormatter extends java.util.logging.Formatter {
r = toAlternate(r); r = toAlternate(r);
} }
if (precision <= 0) { int lc = 0;
precision = Integer.MAX_VALUE; int rc = 0;
} if (precision >= 0) {
lc = minCodePointCount(l, precision);
int fence = Math.min(l.length(), precision); rc = minCodePointCount(r, precision);
if (fence > (precision >> 1)) {
fence = Math.max(fence - r.length(), fence >> 1);
}
if (fence > 0) { if (lc > (precision >> 1)) {
if (fence > l.length() lc = Math.max(lc - rc, lc >> 1);
&& Character.isHighSurrogate(l.charAt(fence - 1))) {
--fence;
} }
l = l.substring(0, fence); rc = Math.min(precision - lc, rc);
l = l.substring(0, l.offsetByCodePoints(0, lc));
r = r.substring(0, r.offsetByCodePoints(0, rc));
} }
r = r.substring(0, Math.min(precision - fence, r.length()));
if (width > 0) { if (width > 0) {
if (precision < 0) {
lc = minCodePointCount(l, width);
rc = minCodePointCount(r, width);
}
final int half = width >> 1; final int half = width >> 1;
if (l.length() < half) { if (lc < half) {
l = pad(flags, l, half); l = pad(flags, l, half - lc);
} }
if (r.length() < half) { if (rc < half) {
r = pad(flags, r, half); r = pad(flags, r, half - rc);
} }
} }
Object[] empty = Collections.emptySet().toArray(); formatter.format(l);
formatter.format(l, empty); if (!l.isEmpty() && !r.isEmpty()) {
if (l.length() != 0 && r.length() != 0) { formatter.format("|");
formatter.format("|", empty); }
formatter.format(r);
}
/**
* Counts the number code points with an upper bound.
*
* @param s the string to count, never null.
* @param limit the max number of code points needed.
* @return the number of code points, never greater than the limit.
*/
private int minCodePointCount(String s, final int limit) {
//assert limit >= 0 : limit;
final int len = s.length();
if ((len - limit) >= limit) {
return limit;
} }
formatter.format(r, empty); return Math.min(s.codePointCount(0, len), limit);
} }
/** /**
@ -841,12 +864,13 @@ public class CompactFormatter extends java.util.logging.Formatter {
* *
* @param flags the formatter flags. * @param flags the formatter flags.
* @param s the string to pad. * @param s the string to pad.
* @param length the final string length. * @param padding the number of spaces to add.
* @return the padded string. * @return the padded string.
*/ */
private String pad(int flags, String s, int length) { private String pad(int flags, String s, int padding) {
final int padding = length - s.length(); //assert padding >= 0 : padding;
final StringBuilder b = new StringBuilder(length); final StringBuilder b = new StringBuilder(
Math.max(s.length() + padding, padding));
if ((flags & java.util.FormattableFlags.LEFT_JUSTIFY) if ((flags & java.util.FormattableFlags.LEFT_JUSTIFY)
== java.util.FormattableFlags.LEFT_JUSTIFY) { == java.util.FormattableFlags.LEFT_JUSTIFY) {
for (int i = 0; i < padding; ++i) { for (int i = 0; i < padding; ++i) {

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2009, 2019 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2019 Jason Mehrens. All rights reserved. * Copyright (c) 2009, 2021 Jason Mehrens. All rights reserved.
* *
* This program and the accompanying materials are made available under the * This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at * terms of the Eclipse Public License v. 2.0, which is available at
@ -55,12 +55,19 @@ final class LogManagerProperties extends Properties {
* Generated serial id. * Generated serial id.
*/ */
private static final long serialVersionUID = -2239983349056806252L; private static final long serialVersionUID = -2239983349056806252L;
/** /**
* Holds the method used to get the LogRecord instant if running on JDK 9 or * Holds the method used to get the LogRecord instant if running on JDK 9 or
* later. * later.
*/ */
private static final Method LR_GET_INSTANT; private static final Method LR_GET_INSTANT;
/**
* Holds the method used to get the long thread id if running on JDK 16 or
* later.
*/
private static final Method LR_GET_LONG_TID;
/** /**
* Holds the method used to get the default time zone if running on JDK 9 or * Holds the method used to get the default time zone if running on JDK 9 or
* later. * later.
@ -73,6 +80,20 @@ final class LogManagerProperties extends Properties {
*/ */
private static final Method ZDT_OF_INSTANT; private static final Method ZDT_OF_INSTANT;
/**
* MethodHandle is available starting at JDK7 and Andriod API 26.
*/
static { //Added in JDK16 see JDK-8245302
Method lrtid = null;
try {
lrtid = LogRecord.class.getMethod("getLongThreadID");
} catch (final RuntimeException ignore) {
} catch (final Exception ignore) { //No need for specific catch.
} catch (final LinkageError ignore) {
}
LR_GET_LONG_TID = lrtid;
}
static { static {
Method lrgi = null; Method lrgi = null;
Method zisd = null; Method zisd = null;
@ -84,17 +105,17 @@ final class LogManagerProperties extends Properties {
zisd = findClass("java.time.ZoneId") zisd = findClass("java.time.ZoneId")
.getMethod("systemDefault"); .getMethod("systemDefault");
if (!Modifier.isStatic(zisd.getModifiers())) { if (!Modifier.isStatic(zisd.getModifiers())) {
zisd = null;
throw new NoSuchMethodException(zisd.toString()); throw new NoSuchMethodException(zisd.toString());
} }
zdtoi = findClass("java.time.ZonedDateTime") zdtoi = findClass("java.time.ZonedDateTime")
.getMethod("ofInstant", findClass("java.time.Instant"), .getMethod("ofInstant", findClass("java.time.Instant"),
findClass("java.time.ZoneId")); findClass("java.time.ZoneId"));
if (!Modifier.isStatic(zdtoi.getModifiers())) { if (!Modifier.isStatic(zdtoi.getModifiers())
throw new NoSuchMethodException(zdtoi.toString()); || !Comparable.class.isAssignableFrom(
} zdtoi.getReturnType())) {
zdtoi = null;
if (!Comparable.class.isAssignableFrom(zdtoi.getReturnType())) {
throw new NoSuchMethodException(zdtoi.toString()); throw new NoSuchMethodException(zdtoi.toString());
} }
} catch (final RuntimeException ignore) { } catch (final RuntimeException ignore) {
@ -112,6 +133,7 @@ final class LogManagerProperties extends Properties {
ZI_SYSTEM_DEFAULT = zisd; ZI_SYSTEM_DEFAULT = zisd;
ZDT_OF_INSTANT = zdtoi; ZDT_OF_INSTANT = zdtoi;
} }
/** /**
* Caches the read only reflection class names string array. Declared * Caches the read only reflection class names string array. Declared
* volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY * volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY
@ -328,6 +350,39 @@ final class LogManagerProperties extends Properties {
return null; return null;
} }
/**
* Gets the long thread id from the given log record.
*
* @param record used to get the long thread id.
* @return null if LogRecord doesn't support long thread ids.
* @throws NullPointerException if record is null.
* @since JavaMail 1.6.7
*/
static Long getLongThreadID(final LogRecord record) {
if (record == null) {
throw new NullPointerException();
}
final Method m = LR_GET_LONG_TID;
if (m != null) {
try {
return (Long) m.invoke(record);
} catch (final InvocationTargetException ite) {
final Throwable cause = ite.getCause();
if (cause instanceof Error) {
throw (Error) cause;
} else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else { //Should never happen.
throw new UndeclaredThrowableException(ite);
}
} catch (final RuntimeException ignore) {
} catch (final Exception ignore) {
}
}
return null;
}
/** /**
* Gets the local host name from the given service. * Gets the local host name from the given service.
* *
@ -365,6 +420,7 @@ final class LogManagerProperties extends Properties {
* *
* @param value an ISO-8601 duration character sequence. * @param value an ISO-8601 duration character sequence.
* @return the number of milliseconds parsed from the duration. * @return the number of milliseconds parsed from the duration.
* @throws ArithmeticException if the duration is too large or too small.
* @throws ClassNotFoundException if the java.time classes are not present. * @throws ClassNotFoundException if the java.time classes are not present.
* @throws IllegalAccessException if the method is inaccessible. * @throws IllegalAccessException if the method is inaccessible.
* @throws InvocationTargetException if the method throws an exception. * @throws InvocationTargetException if the method throws an exception.
@ -378,6 +434,9 @@ final class LogManagerProperties extends Properties {
* @since JavaMail 1.5.5 * @since JavaMail 1.5.5
*/ */
static long parseDurationToMillis(final CharSequence value) throws Exception { static long parseDurationToMillis(final CharSequence value) throws Exception {
if (value == null) {
throw new NullPointerException();
}
try { try {
final Class<?> k = findClass("java.time.Duration"); final Class<?> k = findClass("java.time.Duration");
final Method parse = k.getMethod("parse", CharSequence.class); final Method parse = k.getMethod("parse", CharSequence.class);
@ -395,7 +454,12 @@ final class LogManagerProperties extends Properties {
} catch (final ExceptionInInitializerError EIIE) { } catch (final ExceptionInInitializerError EIIE) {
throw wrapOrThrow(EIIE); throw wrapOrThrow(EIIE);
} catch (final InvocationTargetException ite) { } catch (final InvocationTargetException ite) {
throw paramOrError(ite); final Throwable cause = ite.getCause();
if (cause instanceof ArithmeticException) {
throw (ArithmeticException) cause;
} else {
throw paramOrError(ite);
}
} }
} }
@ -517,9 +581,9 @@ final class LogManagerProperties extends Properties {
} }
Comparator<T> reverse = null; Comparator<T> reverse = null;
//Comparator in Java 1.8 has 'reversed' as a default method. //Comparator in JDK8 has 'reversed' as a default method.
//This code calls that method first to allow custom //This code calls that method first to allow custom
//code to define what reverse order means. //code to define what reverse order means in versions older than JDK8.
try { try {
//assert Modifier.isPublic(c.getClass().getModifiers()) : //assert Modifier.isPublic(c.getClass().getModifiers()) :
// Modifier.toString(c.getClass().getModifiers()); // Modifier.toString(c.getClass().getModifiers());
@ -532,11 +596,9 @@ final class LogManagerProperties extends Properties {
throw wrapOrThrow(eiie); throw wrapOrThrow(eiie);
} }
} }
} catch (final NoSuchMethodException ignore) {
} catch (final IllegalAccessException ignore) {
} catch (final RuntimeException ignore) {
} catch (final InvocationTargetException ite) { } catch (final InvocationTargetException ite) {
paramOrError(ite); //Ignore invocation bugs (returned values). paramOrError(ite); //Ignore invocation bugs (returned values).
} catch (final ReflectiveOperationException | RuntimeException ignore) {
} }
if (reverse == null) { if (reverse == null) {
@ -642,6 +704,8 @@ final class LogManagerProperties extends Properties {
final Class<?> thisClass = LogManagerProperties.class; final Class<?> thisClass = LogManagerProperties.class;
assert Modifier.isFinal(thisClass.getModifiers()) : thisClass; assert Modifier.isFinal(thisClass.getModifiers()) : thisClass;
try { try {
//This code must use reflection to capture extra frames.
//The invoke API doesn't produce the frames needed.
final HashSet<String> traces = new HashSet<>(); final HashSet<String> traces = new HashSet<>();
Throwable t = Throwable.class.getConstructor().newInstance(); Throwable t = Throwable.class.getConstructor().newInstance();
for (StackTraceElement ste : t.getStackTrace()) { for (StackTraceElement ste : t.getStackTrace()) {
@ -652,6 +716,8 @@ final class LogManagerProperties extends Properties {
} }
} }
//This code must use reflection to capture extra frames.
//The invoke API doesn't produce the frames needed.
Throwable.class.getMethod("fillInStackTrace").invoke(t); Throwable.class.getMethod("fillInStackTrace").invoke(t);
for (StackTraceElement ste : t.getStackTrace()) { for (StackTraceElement ste : t.getStackTrace()) {
if (!thisClass.getName().equals(ste.getClassName())) { if (!thisClass.getName().equals(ste.getClassName())) {
@ -756,10 +822,10 @@ final class LogManagerProperties extends Properties {
} }
/** /**
* This code is modified from the LogManager, which explictly states * This code is modified from the LogManager, which explicitly states
* searching the system class loader first, then the context class loader. * searching the system class loader first, then the context class loader.
* There is resistance (compatibility) to change this behavior to simply * There is resistance (compatibility) to change this behavior to simply
* searching the context class loader. * searching the context class loader. See JDK-6878454.
* *
* @param name full class name * @param name full class name
* @return the class. * @return the class.

Loading…
Cancel
Save