/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang.invoke;
import sun.util.logging.
PlatformLogger;
import java.io.
FilePermission;
import java.nio.file.
Files;
import java.nio.file.
InvalidPathException;
import java.nio.file.
Path;
import java.nio.file.
Paths;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.
Objects;
import java.util.concurrent.atomic.
AtomicBoolean;
/**
* Helper class used by InnerClassLambdaMetafactory to log generated classes
*
* @implNote
* <p> Because this class is called by LambdaMetafactory, make use
* of lambda lead to recursive calls cause stack overflow.
*/
final class
ProxyClassesDumper {
private static final char[]
HEX = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private static final char[]
BAD_CHARS = {
'\\', ':', '*', '?', '"', '<', '>', '|'
};
private static final
String[]
REPLACEMENT = {
"%5C", "%3A", "%2A", "%3F", "%22", "%3C", "%3E", "%7C"
};
private final
Path dumpDir;
public static
ProxyClassesDumper getInstance(
String path) {
if (null ==
path) {
return null;
}
try {
path =
path.
trim();
final
Path dir =
Paths.
get(
path.
length() == 0 ? "." :
path);
AccessController.
doPrivileged(new
PrivilegedAction<
Void>() {
@
Override
public
Void run() {
validateDumpDir(
dir);
return null;
}
}, null, new
FilePermission("<<ALL FILES>>", "read, write"));
return new
ProxyClassesDumper(
dir);
} catch (
InvalidPathException ex) {
PlatformLogger.
getLogger(
ProxyClassesDumper.class.
getName())
.
warning("Path " +
path + " is not valid - dumping disabled",
ex);
} catch (
IllegalArgumentException iae) {
PlatformLogger.
getLogger(
ProxyClassesDumper.class.
getName())
.
warning(
iae.
getMessage() + " - dumping disabled");
}
return null;
}
private
ProxyClassesDumper(
Path path) {
dumpDir =
Objects.
requireNonNull(
path);
}
private static void
validateDumpDir(
Path path) {
if (!
Files.
exists(
path)) {
throw new
IllegalArgumentException("Directory " +
path + " does not exist");
} else if (!
Files.
isDirectory(
path)) {
throw new
IllegalArgumentException("Path " +
path + " is not a directory");
} else if (!
Files.
isWritable(
path)) {
throw new
IllegalArgumentException("Directory " +
path + " is not writable");
}
}
public static
String encodeForFilename(
String className) {
final int
len =
className.
length();
StringBuilder sb = new
StringBuilder(
len);
for (int
i = 0;
i <
len;
i++) {
char
c =
className.
charAt(
i);
// control characters
if (
c <= 31) {
sb.
append('%');
sb.
append(
HEX[
c >> 4 & 0x0F]);
sb.
append(
HEX[
c & 0x0F]);
} else {
int
j = 0;
for (;
j <
BAD_CHARS.length;
j++) {
if (
c ==
BAD_CHARS[
j]) {
sb.
append(
REPLACEMENT[
j]);
break;
}
}
if (
j >=
BAD_CHARS.length) {
sb.
append(
c);
}
}
}
return
sb.
toString();
}
public void
dumpClass(
String className, final byte[]
classBytes) {
Path file;
try {
file =
dumpDir.
resolve(
encodeForFilename(
className) + ".class");
} catch (
InvalidPathException ex) {
PlatformLogger.
getLogger(
ProxyClassesDumper.class.
getName())
.
warning("Invalid path for class " +
className);
return;
}
try {
Path dir =
file.
getParent();
Files.
createDirectories(
dir);
Files.
write(
file,
classBytes);
} catch (
Exception ignore) {
PlatformLogger.
getLogger(
ProxyClassesDumper.class.
getName())
.
warning("Exception writing to path at " +
file.
toString());
// simply don't care if this operation failed
}
}
}