init
This commit is contained in:
701
java/org/apache/tomcat/util/Diagnostics.java
Normal file
701
java/org/apache/tomcat/util/Diagnostics.java
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// XXX TODO: Source code line length
|
||||
// XXX TODO: More JavaDoc
|
||||
// XXX Optional: Add support for com.sun.management specific mbean
|
||||
// (http://docs.oracle.com/javase/7/docs/jre/api/management/extension/index.html)
|
||||
// XXX Optional: Wire additional public static methods implemented here
|
||||
// to the manager (think about manager access roles!)
|
||||
// setLoggerLevel(),
|
||||
// setVerboseClassLoading(),
|
||||
// setThreadContentionMonitoringEnabled(),
|
||||
// setThreadCpuTimeEnabled(),
|
||||
// resetPeakThreadCount(),
|
||||
// setVerboseGarbageCollection()
|
||||
// gc(),
|
||||
// resetPeakUsage(),
|
||||
// setUsageThreshold(),
|
||||
// setCollectionUsageThreshold()
|
||||
|
||||
package org.apache.tomcat.util;
|
||||
|
||||
import java.lang.management.ClassLoadingMXBean;
|
||||
import java.lang.management.CompilationMXBean;
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.LockInfo;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryManagerMXBean;
|
||||
import java.lang.management.MemoryPoolMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.lang.management.MonitorInfo;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
import java.lang.management.PlatformLoggingMXBean;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
public class Diagnostics {
|
||||
|
||||
private static final String PACKAGE = "org.apache.tomcat.util";
|
||||
private static final StringManager sm = StringManager.getManager(PACKAGE);
|
||||
|
||||
private static final String INDENT1 = " ";
|
||||
private static final String INDENT2 = "\t";
|
||||
private static final String INDENT3 = " ";
|
||||
private static final String CRLF = "\r\n";
|
||||
private static final String vminfoSystemProperty = "java.vm.info";
|
||||
|
||||
private static final Log log = LogFactory.getLog(Diagnostics.class);
|
||||
|
||||
private static final SimpleDateFormat timeformat =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
/* Some platform MBeans */
|
||||
private static final ClassLoadingMXBean classLoadingMXBean =
|
||||
ManagementFactory.getClassLoadingMXBean();
|
||||
private static final CompilationMXBean compilationMXBean =
|
||||
ManagementFactory.getCompilationMXBean();
|
||||
private static final OperatingSystemMXBean operatingSystemMXBean =
|
||||
ManagementFactory.getOperatingSystemMXBean();
|
||||
private static final RuntimeMXBean runtimeMXBean =
|
||||
ManagementFactory.getRuntimeMXBean();
|
||||
private static final ThreadMXBean threadMXBean =
|
||||
ManagementFactory.getThreadMXBean();
|
||||
|
||||
// XXX Not sure whether the following MBeans should better
|
||||
// be retrieved on demand, i.e. whether they can change
|
||||
// dynamically in the MBeanServer.
|
||||
private static final PlatformLoggingMXBean loggingMXBean =
|
||||
ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class);
|
||||
private static final MemoryMXBean memoryMXBean =
|
||||
ManagementFactory.getMemoryMXBean();
|
||||
private static final List<GarbageCollectorMXBean> garbageCollectorMXBeans =
|
||||
ManagementFactory.getGarbageCollectorMXBeans();
|
||||
private static final List<MemoryManagerMXBean> memoryManagerMXBeans =
|
||||
ManagementFactory.getMemoryManagerMXBeans();
|
||||
private static final List<MemoryPoolMXBean> memoryPoolMXBeans =
|
||||
ManagementFactory.getMemoryPoolMXBeans();
|
||||
|
||||
/**
|
||||
* Check whether thread contention monitoring is enabled.
|
||||
*
|
||||
* @return true if thread contention monitoring is enabled
|
||||
*/
|
||||
public static boolean isThreadContentionMonitoringEnabled() {
|
||||
return threadMXBean.isThreadContentionMonitoringEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable thread contention monitoring via the ThreadMxMXBean.
|
||||
*
|
||||
* @param enable whether to enable thread contention monitoring
|
||||
*/
|
||||
public static void setThreadContentionMonitoringEnabled(boolean enable) {
|
||||
threadMXBean.setThreadContentionMonitoringEnabled(enable);
|
||||
boolean checkValue = threadMXBean.isThreadContentionMonitoringEnabled();
|
||||
if (enable != checkValue) {
|
||||
log.error("Could not set threadContentionMonitoringEnabled to " +
|
||||
enable + ", got " + checkValue + " instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether thread cpu time measurement is enabled.
|
||||
*
|
||||
* @return true if thread cpu time measurement is enabled
|
||||
*/
|
||||
public static boolean isThreadCpuTimeEnabled() {
|
||||
return threadMXBean.isThreadCpuTimeEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable thread cpu time measurement via the ThreadMxMXBean.
|
||||
*
|
||||
* @param enable whether to enable thread cpu time measurement
|
||||
*/
|
||||
public static void setThreadCpuTimeEnabled(boolean enable) {
|
||||
threadMXBean.setThreadCpuTimeEnabled(enable);
|
||||
boolean checkValue = threadMXBean.isThreadCpuTimeEnabled();
|
||||
if (enable != checkValue) {
|
||||
log.error("Could not set threadCpuTimeEnabled to " + enable +
|
||||
", got " + checkValue + " instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset peak thread count in ThreadMXBean
|
||||
*/
|
||||
public static void resetPeakThreadCount() {
|
||||
threadMXBean.resetPeakThreadCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set verbose class loading
|
||||
*
|
||||
* @param verbose whether to enable verbose class loading
|
||||
*/
|
||||
public static void setVerboseClassLoading(boolean verbose) {
|
||||
classLoadingMXBean.setVerbose(verbose);
|
||||
boolean checkValue = classLoadingMXBean.isVerbose();
|
||||
if (verbose != checkValue) {
|
||||
log.error("Could not set verbose class loading to " + verbose +
|
||||
", got " + checkValue + " instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set logger level
|
||||
*
|
||||
* @param loggerName the name of the logger
|
||||
* @param levelName the level to set
|
||||
*/
|
||||
public static void setLoggerLevel(String loggerName, String levelName) {
|
||||
loggingMXBean.setLoggerLevel(loggerName, levelName);
|
||||
String checkValue = loggingMXBean.getLoggerLevel(loggerName);
|
||||
if (!checkValue.equals(levelName)) {
|
||||
log.error("Could not set logger level for logger '" +
|
||||
loggerName + "' to '" + levelName +
|
||||
"', got '" + checkValue + "' instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set verbose garbage collection logging
|
||||
*
|
||||
* @param verbose whether to enable verbose gc logging
|
||||
*/
|
||||
public static void setVerboseGarbageCollection(boolean verbose) {
|
||||
memoryMXBean.setVerbose(verbose);
|
||||
boolean checkValue = memoryMXBean.isVerbose();
|
||||
if (verbose != checkValue) {
|
||||
log.error("Could not set verbose garbage collection logging to " + verbose +
|
||||
", got " + checkValue + " instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate garbage collection via MX Bean
|
||||
*/
|
||||
public static void gc() {
|
||||
memoryMXBean.gc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset peak memory usage data in MemoryPoolMXBean
|
||||
*
|
||||
* @param name name of the MemoryPoolMXBean or "all"
|
||||
*/
|
||||
public static void resetPeakUsage(String name) {
|
||||
for (MemoryPoolMXBean mbean: memoryPoolMXBeans) {
|
||||
if (name.equals("all") || name.equals(mbean.getName())) {
|
||||
mbean.resetPeakUsage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set usage threshold in MemoryPoolMXBean
|
||||
*
|
||||
* @param name name of the MemoryPoolMXBean
|
||||
* @param threshold the threshold to set
|
||||
* @return true if setting the threshold succeeded
|
||||
*/
|
||||
public static boolean setUsageThreshold(String name, long threshold) {
|
||||
for (MemoryPoolMXBean mbean: memoryPoolMXBeans) {
|
||||
if (name.equals(mbean.getName())) {
|
||||
try {
|
||||
mbean.setUsageThreshold(threshold);
|
||||
return true;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// IGNORE
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set collection usage threshold in MemoryPoolMXBean
|
||||
*
|
||||
* @param name name of the MemoryPoolMXBean
|
||||
* @param threshold the collection threshold to set
|
||||
* @return true if setting the threshold succeeded
|
||||
*/
|
||||
public static boolean setCollectionUsageThreshold(String name, long threshold) {
|
||||
for (MemoryPoolMXBean mbean: memoryPoolMXBeans) {
|
||||
if (name.equals(mbean.getName())) {
|
||||
try {
|
||||
mbean.setCollectionUsageThreshold(threshold);
|
||||
return true;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// IGNORE
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the thread dump header for one thread.
|
||||
*
|
||||
* @param ti the ThreadInfo describing the thread
|
||||
* @return the formatted thread dump header
|
||||
*/
|
||||
private static String getThreadDumpHeader(ThreadInfo ti) {
|
||||
StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"");
|
||||
sb.append(" Id=" + ti.getThreadId());
|
||||
sb.append(" cpu=" + threadMXBean.getThreadCpuTime(ti.getThreadId()) +
|
||||
" ns");
|
||||
sb.append(" usr=" + threadMXBean.getThreadUserTime(ti.getThreadId()) +
|
||||
" ns");
|
||||
sb.append(" blocked " + ti.getBlockedCount() + " for " +
|
||||
ti.getBlockedTime() + " ms");
|
||||
sb.append(" waited " + ti.getWaitedCount() + " for " +
|
||||
ti.getWaitedTime() + " ms");
|
||||
|
||||
if (ti.isSuspended()) {
|
||||
sb.append(" (suspended)");
|
||||
}
|
||||
if (ti.isInNative()) {
|
||||
sb.append(" (running in native)");
|
||||
}
|
||||
sb.append(CRLF);
|
||||
sb.append(INDENT3 + "java.lang.Thread.State: " + ti.getThreadState());
|
||||
sb.append(CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the thread dump for one thread.
|
||||
*
|
||||
* @param ti the ThreadInfo describing the thread
|
||||
* @return the formatted thread dump
|
||||
*/
|
||||
private static String getThreadDump(ThreadInfo ti) {
|
||||
StringBuilder sb = new StringBuilder(getThreadDumpHeader(ti));
|
||||
for (LockInfo li : ti.getLockedSynchronizers()) {
|
||||
sb.append(INDENT2 + "locks " +
|
||||
li.toString() + CRLF);
|
||||
}
|
||||
boolean start = true;
|
||||
StackTraceElement[] stes = ti.getStackTrace();
|
||||
Object[] monitorDepths = new Object[stes.length];
|
||||
MonitorInfo[] mis = ti.getLockedMonitors();
|
||||
for (int i = 0; i < mis.length; i++) {
|
||||
monitorDepths[mis[i].getLockedStackDepth()] = mis[i];
|
||||
}
|
||||
for (int i = 0; i < stes.length; i++) {
|
||||
StackTraceElement ste = stes[i];
|
||||
sb.append(INDENT2 +
|
||||
"at " + ste.toString() + CRLF);
|
||||
if (start) {
|
||||
if (ti.getLockName() != null) {
|
||||
sb.append(INDENT2 + "- waiting on (a " +
|
||||
ti.getLockName() + ")");
|
||||
if (ti.getLockOwnerName() != null) {
|
||||
sb.append(" owned by " + ti.getLockOwnerName() +
|
||||
" Id=" + ti.getLockOwnerId());
|
||||
}
|
||||
sb.append(CRLF);
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
if (monitorDepths[i] != null) {
|
||||
MonitorInfo mi = (MonitorInfo)monitorDepths[i];
|
||||
sb.append(INDENT2 +
|
||||
"- locked (a " + mi.toString() + ")"+
|
||||
" index " + mi.getLockedStackDepth() +
|
||||
" frame " + mi.getLockedStackFrame().toString());
|
||||
sb.append(CRLF);
|
||||
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the thread dump for a list of threads.
|
||||
*
|
||||
* @param tinfos the ThreadInfo array describing the thread list
|
||||
* @return the formatted thread dump
|
||||
*/
|
||||
private static String getThreadDump(ThreadInfo[] tinfos) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (ThreadInfo tinfo : tinfos) {
|
||||
sb.append(getThreadDump(tinfo));
|
||||
sb.append(CRLF);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any threads are deadlocked. If any, print
|
||||
* the thread dump for those threads.
|
||||
*
|
||||
* @return a deadlock message and the formatted thread dump
|
||||
* of the deadlocked threads
|
||||
*/
|
||||
public static String findDeadlock() {
|
||||
ThreadInfo[] tinfos = null;
|
||||
long[] ids = threadMXBean.findDeadlockedThreads();
|
||||
if (ids != null) {
|
||||
tinfos = threadMXBean.getThreadInfo(threadMXBean.findDeadlockedThreads(),
|
||||
true, true);
|
||||
if (tinfos != null) {
|
||||
StringBuilder sb =
|
||||
new StringBuilder("Deadlock found between the following threads:");
|
||||
sb.append(CRLF);
|
||||
sb.append(getThreadDump(tinfos));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a formatted JVM thread dump.
|
||||
* The default StringManager will be used.
|
||||
*
|
||||
* @return the formatted JVM thread dump
|
||||
*/
|
||||
public static String getThreadDump() {
|
||||
return getThreadDump(sm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a formatted JVM thread dump.
|
||||
* The given list of locales will be used
|
||||
* to retrieve a StringManager.
|
||||
*
|
||||
* @param requestedLocales list of locales to use
|
||||
* @return the formatted JVM thread dump
|
||||
*/
|
||||
public static String getThreadDump(Enumeration<Locale> requestedLocales) {
|
||||
return getThreadDump(
|
||||
StringManager.getManager(PACKAGE, requestedLocales));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a JVM thread dump formatted
|
||||
* using the given StringManager.
|
||||
*
|
||||
* @param requestedSm the StringManager to use
|
||||
* @return the formatted JVM thread dump
|
||||
*/
|
||||
public static String getThreadDump(StringManager requestedSm) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
synchronized(timeformat) {
|
||||
sb.append(timeformat.format(new Date()));
|
||||
}
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.threadDumpTitle"));
|
||||
sb.append(" ");
|
||||
sb.append(runtimeMXBean.getVmName());
|
||||
sb.append(" (");
|
||||
sb.append(runtimeMXBean.getVmVersion());
|
||||
String vminfo = System.getProperty(vminfoSystemProperty);
|
||||
if (vminfo != null) {
|
||||
sb.append(" " + vminfo);
|
||||
}
|
||||
sb.append("):" + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
ThreadInfo[] tis = threadMXBean.dumpAllThreads(true, true);
|
||||
sb.append(getThreadDump(tis));
|
||||
|
||||
sb.append(findDeadlock());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format contents of a MemoryUsage object.
|
||||
* @param name a text prefix used in formatting
|
||||
* @param usage the MemoryUsage object to format
|
||||
* @return the formatted contents
|
||||
*/
|
||||
private static String formatMemoryUsage(String name, MemoryUsage usage) {
|
||||
if (usage != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(INDENT1 + name + " init: " + usage.getInit() + CRLF);
|
||||
sb.append(INDENT1 + name + " used: " + usage.getUsed() + CRLF);
|
||||
sb.append(INDENT1 + name + " committed: " + usage.getCommitted() + CRLF);
|
||||
sb.append(INDENT1 + name + " max: " + usage.getMax() + CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a formatted JVM information text.
|
||||
* The default StringManager will be used.
|
||||
*
|
||||
* @return the formatted JVM information text
|
||||
*/
|
||||
public static String getVMInfo() {
|
||||
return getVMInfo(sm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a formatted JVM information text.
|
||||
* The given list of locales will be used
|
||||
* to retrieve a StringManager.
|
||||
*
|
||||
* @param requestedLocales list of locales to use
|
||||
* @return the formatted JVM information text
|
||||
*/
|
||||
public static String getVMInfo(Enumeration<Locale> requestedLocales) {
|
||||
return getVMInfo(StringManager.getManager(PACKAGE, requestedLocales));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a JVM information text formatted
|
||||
* using the given StringManager.
|
||||
*
|
||||
* @param requestedSm the StringManager to use
|
||||
* @return the formatted JVM information text
|
||||
*/
|
||||
public static String getVMInfo(StringManager requestedSm) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
synchronized(timeformat) {
|
||||
sb.append(timeformat.format(new Date()));
|
||||
}
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoRuntime"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "vmName: " + runtimeMXBean.getVmName() + CRLF);
|
||||
sb.append(INDENT1 + "vmVersion: " + runtimeMXBean.getVmVersion() + CRLF);
|
||||
sb.append(INDENT1 + "vmVendor: " + runtimeMXBean.getVmVendor() + CRLF);
|
||||
sb.append(INDENT1 + "specName: " + runtimeMXBean.getSpecName() + CRLF);
|
||||
sb.append(INDENT1 + "specVersion: " + runtimeMXBean.getSpecVersion() + CRLF);
|
||||
sb.append(INDENT1 + "specVendor: " + runtimeMXBean.getSpecVendor() + CRLF);
|
||||
sb.append(INDENT1 + "managementSpecVersion: " +
|
||||
runtimeMXBean.getManagementSpecVersion() + CRLF);
|
||||
sb.append(INDENT1 + "name: " + runtimeMXBean.getName() + CRLF);
|
||||
sb.append(INDENT1 + "startTime: " + runtimeMXBean.getStartTime() + CRLF);
|
||||
sb.append(INDENT1 + "uptime: " + runtimeMXBean.getUptime() + CRLF);
|
||||
sb.append(INDENT1 + "isBootClassPathSupported: " +
|
||||
runtimeMXBean.isBootClassPathSupported() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoOs"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "name: " + operatingSystemMXBean.getName() + CRLF);
|
||||
sb.append(INDENT1 + "version: " + operatingSystemMXBean.getVersion() + CRLF);
|
||||
sb.append(INDENT1 + "architecture: " + operatingSystemMXBean.getArch() + CRLF);
|
||||
sb.append(INDENT1 + "availableProcessors: " +
|
||||
operatingSystemMXBean.getAvailableProcessors() + CRLF);
|
||||
sb.append(INDENT1 + "systemLoadAverage: " +
|
||||
operatingSystemMXBean.getSystemLoadAverage() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoThreadMxBean"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "isCurrentThreadCpuTimeSupported: " +
|
||||
threadMXBean.isCurrentThreadCpuTimeSupported() + CRLF);
|
||||
sb.append(INDENT1 + "isThreadCpuTimeSupported: " +
|
||||
threadMXBean.isThreadCpuTimeSupported() + CRLF);
|
||||
sb.append(INDENT1 + "isThreadCpuTimeEnabled: " +
|
||||
threadMXBean.isThreadCpuTimeEnabled() + CRLF);
|
||||
sb.append(INDENT1 + "isObjectMonitorUsageSupported: " +
|
||||
threadMXBean.isObjectMonitorUsageSupported() + CRLF);
|
||||
sb.append(INDENT1 + "isSynchronizerUsageSupported: " +
|
||||
threadMXBean.isSynchronizerUsageSupported() + CRLF);
|
||||
sb.append(INDENT1 + "isThreadContentionMonitoringSupported: " +
|
||||
threadMXBean.isThreadContentionMonitoringSupported() + CRLF);
|
||||
sb.append(INDENT1 + "isThreadContentionMonitoringEnabled: " +
|
||||
threadMXBean.isThreadContentionMonitoringEnabled() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoThreadCounts"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "daemon: " + threadMXBean.getDaemonThreadCount() + CRLF);
|
||||
sb.append(INDENT1 + "total: " + threadMXBean.getThreadCount() + CRLF);
|
||||
sb.append(INDENT1 + "peak: " + threadMXBean.getPeakThreadCount() + CRLF);
|
||||
sb.append(INDENT1 + "totalStarted: " +
|
||||
threadMXBean.getTotalStartedThreadCount() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoStartup"));
|
||||
sb.append(":" + CRLF);
|
||||
for (String arg: runtimeMXBean.getInputArguments()) {
|
||||
sb.append(INDENT1 + arg + CRLF);
|
||||
}
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoPath"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "bootClassPath: " + runtimeMXBean.getBootClassPath() + CRLF);
|
||||
sb.append(INDENT1 + "classPath: " + runtimeMXBean.getClassPath() + CRLF);
|
||||
sb.append(INDENT1 + "libraryPath: " + runtimeMXBean.getLibraryPath() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoClassLoading"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "loaded: " +
|
||||
classLoadingMXBean.getLoadedClassCount() + CRLF);
|
||||
sb.append(INDENT1 + "unloaded: " +
|
||||
classLoadingMXBean.getUnloadedClassCount() + CRLF);
|
||||
sb.append(INDENT1 + "totalLoaded: " +
|
||||
classLoadingMXBean.getTotalLoadedClassCount() + CRLF);
|
||||
sb.append(INDENT1 + "isVerbose: " +
|
||||
classLoadingMXBean.isVerbose() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoClassCompilation"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "name: " + compilationMXBean.getName() + CRLF);
|
||||
sb.append(INDENT1 + "totalCompilationTime: " +
|
||||
compilationMXBean.getTotalCompilationTime() + CRLF);
|
||||
sb.append(INDENT1 + "isCompilationTimeMonitoringSupported: " +
|
||||
compilationMXBean.isCompilationTimeMonitoringSupported() + CRLF);
|
||||
sb.append(CRLF);
|
||||
|
||||
for (MemoryManagerMXBean mbean: memoryManagerMXBeans) {
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoMemoryManagers", mbean.getName()));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "isValid: " + mbean.isValid() + CRLF);
|
||||
sb.append(INDENT1 + "mbean.getMemoryPoolNames: " + CRLF);
|
||||
String[] names = mbean.getMemoryPoolNames();
|
||||
Arrays.sort(names);
|
||||
for (String name: names) {
|
||||
sb.append(INDENT2 + name + CRLF);
|
||||
}
|
||||
sb.append(CRLF);
|
||||
}
|
||||
|
||||
for (GarbageCollectorMXBean mbean: garbageCollectorMXBeans) {
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoGarbageCollectors", mbean.getName()));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "isValid: " + mbean.isValid() + CRLF);
|
||||
sb.append(INDENT1 + "mbean.getMemoryPoolNames: " + CRLF);
|
||||
String[] names = mbean.getMemoryPoolNames();
|
||||
Arrays.sort(names);
|
||||
for (String name: names) {
|
||||
sb.append(INDENT2 + name + CRLF);
|
||||
}
|
||||
sb.append(INDENT1 + "getCollectionCount: " + mbean.getCollectionCount() + CRLF);
|
||||
sb.append(INDENT1 + "getCollectionTime: " + mbean.getCollectionTime() + CRLF);
|
||||
sb.append(CRLF);
|
||||
}
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoMemory"));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "isVerbose: " + memoryMXBean.isVerbose() + CRLF);
|
||||
sb.append(INDENT1 + "getObjectPendingFinalizationCount: " + memoryMXBean.getObjectPendingFinalizationCount() + CRLF);
|
||||
sb.append(formatMemoryUsage("heap", memoryMXBean.getHeapMemoryUsage()));
|
||||
sb.append(formatMemoryUsage("non-heap", memoryMXBean.getNonHeapMemoryUsage()));
|
||||
sb.append(CRLF);
|
||||
|
||||
for (MemoryPoolMXBean mbean: memoryPoolMXBeans) {
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoMemoryPools", mbean.getName()));
|
||||
sb.append(":" + CRLF);
|
||||
sb.append(INDENT1 + "isValid: " + mbean.isValid() + CRLF);
|
||||
sb.append(INDENT1 + "getType: " + mbean.getType() + CRLF);
|
||||
sb.append(INDENT1 + "mbean.getMemoryManagerNames: " + CRLF);
|
||||
String[] names = mbean.getMemoryManagerNames();
|
||||
Arrays.sort(names);
|
||||
for (String name: names) {
|
||||
sb.append(INDENT2 + name + CRLF);
|
||||
}
|
||||
sb.append(INDENT1 + "isUsageThresholdSupported: " + mbean.isUsageThresholdSupported() + CRLF);
|
||||
try {
|
||||
sb.append(INDENT1 + "isUsageThresholdExceeded: " + mbean.isUsageThresholdExceeded() + CRLF);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
sb.append(INDENT1 + "isCollectionUsageThresholdSupported: " + mbean.isCollectionUsageThresholdSupported() + CRLF);
|
||||
try {
|
||||
sb.append(INDENT1 + "isCollectionUsageThresholdExceeded: " + mbean.isCollectionUsageThresholdExceeded() + CRLF);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
try {
|
||||
sb.append(INDENT1 + "getUsageThreshold: " + mbean.getUsageThreshold() + CRLF);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
try {
|
||||
sb.append(INDENT1 + "getUsageThresholdCount: " + mbean.getUsageThresholdCount() + CRLF);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
try {
|
||||
sb.append(INDENT1 + "getCollectionUsageThreshold: " + mbean.getCollectionUsageThreshold() + CRLF);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
try {
|
||||
sb.append(INDENT1 + "getCollectionUsageThresholdCount: " + mbean.getCollectionUsageThresholdCount() + CRLF);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// IGNORE
|
||||
}
|
||||
sb.append(formatMemoryUsage("current", mbean.getUsage()));
|
||||
sb.append(formatMemoryUsage("collection", mbean.getCollectionUsage()));
|
||||
sb.append(formatMemoryUsage("peak", mbean.getPeakUsage()));
|
||||
sb.append(CRLF);
|
||||
}
|
||||
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoSystem"));
|
||||
sb.append(":" + CRLF);
|
||||
Map<String,String> props = runtimeMXBean.getSystemProperties();
|
||||
ArrayList<String> keys = new ArrayList<>(props.keySet());
|
||||
Collections.sort(keys);
|
||||
for (String prop: keys) {
|
||||
sb.append(INDENT1 + prop + ": " + props.get(prop) + CRLF);
|
||||
}
|
||||
sb.append(CRLF);
|
||||
|
||||
sb.append(requestedSm.getString("diagnostics.vmInfoLogger"));
|
||||
sb.append(":" + CRLF);
|
||||
List<String> loggers = loggingMXBean.getLoggerNames();
|
||||
Collections.sort(loggers);
|
||||
for (String logger: loggers) {
|
||||
sb.append(INDENT1 + logger +
|
||||
": level=" + loggingMXBean.getLoggerLevel(logger) +
|
||||
", parent=" + loggingMXBean.getParentLoggerName(logger) + CRLF);
|
||||
}
|
||||
sb.append(CRLF);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
71
java/org/apache/tomcat/util/ExceptionUtils.java
Normal file
71
java/org/apache/tomcat/util/ExceptionUtils.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for handling Throwables and Exceptions.
|
||||
*/
|
||||
public class ExceptionUtils {
|
||||
|
||||
/**
|
||||
* Checks whether the supplied Throwable is one that needs to be
|
||||
* rethrown and swallows all others.
|
||||
* @param t the Throwable to check
|
||||
*/
|
||||
public static void handleThrowable(Throwable t) {
|
||||
if (t instanceof ThreadDeath) {
|
||||
throw (ThreadDeath) t;
|
||||
}
|
||||
if (t instanceof StackOverflowError) {
|
||||
// Swallow silently - it should be recoverable
|
||||
return;
|
||||
}
|
||||
if (t instanceof VirtualMachineError) {
|
||||
throw (VirtualMachineError) t;
|
||||
}
|
||||
// All other instances of Throwable will be silently swallowed
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the supplied Throwable is an instance of
|
||||
* <code>InvocationTargetException</code> and returns the throwable that is
|
||||
* wrapped by it, if there is any.
|
||||
*
|
||||
* @param t the Throwable to check
|
||||
* @return <code>t</code> or <code>t.getCause()</code>
|
||||
*/
|
||||
public static Throwable unwrapInvocationTargetException(Throwable t) {
|
||||
if (t instanceof InvocationTargetException && t.getCause() != null) {
|
||||
return t.getCause();
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NO-OP method provided to enable simple pre-loading of this class. Since
|
||||
* the class is used extensively in error handling, it is prudent to
|
||||
* pre-load it to avoid any failure to load this class masking the true
|
||||
* problem during error handling.
|
||||
*/
|
||||
public static void preload() {
|
||||
// NO-OP
|
||||
}
|
||||
}
|
||||
538
java/org/apache/tomcat/util/IntrospectionUtils.java
Normal file
538
java/org/apache/tomcat/util/IntrospectionUtils.java
Normal file
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.security.PermissionCheck;
|
||||
|
||||
/**
|
||||
* Utils for introspection and reflection
|
||||
*/
|
||||
public final class IntrospectionUtils {
|
||||
|
||||
private static final Log log = LogFactory.getLog(IntrospectionUtils.class);
|
||||
|
||||
/**
|
||||
* Find a method with the right name If found, call the method ( if param is
|
||||
* int or boolean we'll convert value to the right type before) - that means
|
||||
* you can have setDebug(1).
|
||||
* @param o The object to set a property on
|
||||
* @param name The property name
|
||||
* @param value The property value
|
||||
* @return <code>true</code> if operation was successful
|
||||
*/
|
||||
public static boolean setProperty(Object o, String name, String value) {
|
||||
return setProperty(o,name,value,true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("null") // setPropertyMethodVoid is not null when used
|
||||
public static boolean setProperty(Object o, String name, String value,
|
||||
boolean invokeSetProperty) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: setProperty(" +
|
||||
o.getClass() + " " + name + "=" + value + ")");
|
||||
|
||||
String setter = "set" + capitalize(name);
|
||||
|
||||
try {
|
||||
Method methods[] = findMethods(o.getClass());
|
||||
Method setPropertyMethodVoid = null;
|
||||
Method setPropertyMethodBool = null;
|
||||
|
||||
// First, the ideal case - a setFoo( String ) method
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Class<?> paramT[] = methods[i].getParameterTypes();
|
||||
if (setter.equals(methods[i].getName()) && paramT.length == 1
|
||||
&& "java.lang.String".equals(paramT[0].getName())) {
|
||||
|
||||
methods[i].invoke(o, new Object[] { value });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try a setFoo ( int ) or ( boolean )
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
boolean ok = true;
|
||||
if (setter.equals(methods[i].getName())
|
||||
&& methods[i].getParameterTypes().length == 1) {
|
||||
|
||||
// match - find the type and invoke it
|
||||
Class<?> paramType = methods[i].getParameterTypes()[0];
|
||||
Object params[] = new Object[1];
|
||||
|
||||
// Try a setFoo ( int )
|
||||
if ("java.lang.Integer".equals(paramType.getName())
|
||||
|| "int".equals(paramType.getName())) {
|
||||
try {
|
||||
params[0] = Integer.valueOf(value);
|
||||
} catch (NumberFormatException ex) {
|
||||
ok = false;
|
||||
}
|
||||
// Try a setFoo ( long )
|
||||
}else if ("java.lang.Long".equals(paramType.getName())
|
||||
|| "long".equals(paramType.getName())) {
|
||||
try {
|
||||
params[0] = Long.valueOf(value);
|
||||
} catch (NumberFormatException ex) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// Try a setFoo ( boolean )
|
||||
} else if ("java.lang.Boolean".equals(paramType.getName())
|
||||
|| "boolean".equals(paramType.getName())) {
|
||||
params[0] = Boolean.valueOf(value);
|
||||
|
||||
// Try a setFoo ( InetAddress )
|
||||
} else if ("java.net.InetAddress".equals(paramType
|
||||
.getName())) {
|
||||
try {
|
||||
params[0] = InetAddress.getByName(value);
|
||||
} catch (UnknownHostException exc) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: Unable to resolve host name:" + value);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// Unknown type
|
||||
} else {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: Unknown type " +
|
||||
paramType.getName());
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
methods[i].invoke(o, params);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// save "setProperty" for later
|
||||
if ("setProperty".equals(methods[i].getName())) {
|
||||
if (methods[i].getReturnType()==Boolean.TYPE){
|
||||
setPropertyMethodBool = methods[i];
|
||||
}else {
|
||||
setPropertyMethodVoid = methods[i];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, no setXXX found, try a setProperty("name", "value")
|
||||
if (invokeSetProperty && (setPropertyMethodBool != null ||
|
||||
setPropertyMethodVoid != null)) {
|
||||
Object params[] = new Object[2];
|
||||
params[0] = name;
|
||||
params[1] = value;
|
||||
if (setPropertyMethodBool != null) {
|
||||
try {
|
||||
return ((Boolean) setPropertyMethodBool.invoke(o,
|
||||
params)).booleanValue();
|
||||
}catch (IllegalArgumentException biae) {
|
||||
//the boolean method had the wrong
|
||||
//parameter types. lets try the other
|
||||
if (setPropertyMethodVoid!=null) {
|
||||
setPropertyMethodVoid.invoke(o, params);
|
||||
return true;
|
||||
}else {
|
||||
throw biae;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setPropertyMethodVoid.invoke(o, params);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException ex2) {
|
||||
log.warn("IAE " + o + " " + name + " " + value, ex2);
|
||||
} catch (SecurityException ex1) {
|
||||
log.warn("IntrospectionUtils: SecurityException for " +
|
||||
o.getClass() + " " + name + "=" + value + ")", ex1);
|
||||
} catch (IllegalAccessException iae) {
|
||||
log.warn("IntrospectionUtils: IllegalAccessException for " +
|
||||
o.getClass() + " " + name + "=" + value + ")", iae);
|
||||
} catch (InvocationTargetException ie) {
|
||||
ExceptionUtils.handleThrowable(ie.getCause());
|
||||
log.warn("IntrospectionUtils: InvocationTargetException for " +
|
||||
o.getClass() + " " + name + "=" + value + ")", ie);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Object getProperty(Object o, String name) {
|
||||
String getter = "get" + capitalize(name);
|
||||
String isGetter = "is" + capitalize(name);
|
||||
|
||||
try {
|
||||
Method methods[] = findMethods(o.getClass());
|
||||
Method getPropertyMethod = null;
|
||||
|
||||
// First, the ideal case - a getFoo() method
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
Class<?> paramT[] = methods[i].getParameterTypes();
|
||||
if (getter.equals(methods[i].getName()) && paramT.length == 0) {
|
||||
return methods[i].invoke(o, (Object[]) null);
|
||||
}
|
||||
if (isGetter.equals(methods[i].getName()) && paramT.length == 0) {
|
||||
return methods[i].invoke(o, (Object[]) null);
|
||||
}
|
||||
|
||||
if ("getProperty".equals(methods[i].getName())) {
|
||||
getPropertyMethod = methods[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, no setXXX found, try a getProperty("name")
|
||||
if (getPropertyMethod != null) {
|
||||
Object params[] = new Object[1];
|
||||
params[0] = name;
|
||||
return getPropertyMethod.invoke(o, params);
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException ex2) {
|
||||
log.warn("IAE " + o + " " + name, ex2);
|
||||
} catch (SecurityException ex1) {
|
||||
log.warn("IntrospectionUtils: SecurityException for " +
|
||||
o.getClass() + " " + name + ")", ex1);
|
||||
} catch (IllegalAccessException iae) {
|
||||
log.warn("IntrospectionUtils: IllegalAccessException for " +
|
||||
o.getClass() + " " + name + ")", iae);
|
||||
} catch (InvocationTargetException ie) {
|
||||
if (ie.getCause() instanceof NullPointerException) {
|
||||
// Assume the underlying object uses a storage to represent an unset property
|
||||
return null;
|
||||
}
|
||||
ExceptionUtils.handleThrowable(ie.getCause());
|
||||
log.warn("IntrospectionUtils: InvocationTargetException for " +
|
||||
o.getClass() + " " + name + ")", ie);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace ${NAME} with the property value.
|
||||
* @param value The value
|
||||
* @param staticProp Replacement properties
|
||||
* @param dynamicProp Replacement properties
|
||||
* @return the replacement value
|
||||
* @deprecated Use {@link #replaceProperties(String, Hashtable, PropertySource[], ClassLoader)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static String replaceProperties(String value,
|
||||
Hashtable<Object,Object> staticProp, PropertySource dynamicProp[]) {
|
||||
return replaceProperties(value, staticProp, dynamicProp, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace ${NAME} with the property value.
|
||||
* @param value The value
|
||||
* @param staticProp Replacement properties
|
||||
* @param dynamicProp Replacement properties
|
||||
* @param classLoader Class loader associated with the code requesting the
|
||||
* property
|
||||
* @return the replacement value
|
||||
*/
|
||||
public static String replaceProperties(String value,
|
||||
Hashtable<Object,Object> staticProp, PropertySource dynamicProp[],
|
||||
ClassLoader classLoader) {
|
||||
|
||||
if (value.indexOf('$') < 0) {
|
||||
return value;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int prev = 0;
|
||||
// assert value!=nil
|
||||
int pos;
|
||||
while ((pos = value.indexOf('$', prev)) >= 0) {
|
||||
if (pos > 0) {
|
||||
sb.append(value.substring(prev, pos));
|
||||
}
|
||||
if (pos == (value.length() - 1)) {
|
||||
sb.append('$');
|
||||
prev = pos + 1;
|
||||
} else if (value.charAt(pos + 1) != '{') {
|
||||
sb.append('$');
|
||||
prev = pos + 1; // XXX
|
||||
} else {
|
||||
int endName = value.indexOf('}', pos);
|
||||
if (endName < 0) {
|
||||
sb.append(value.substring(pos));
|
||||
prev = value.length();
|
||||
continue;
|
||||
}
|
||||
String n = value.substring(pos + 2, endName);
|
||||
String v = null;
|
||||
if (staticProp != null) {
|
||||
v = (String) staticProp.get(n);
|
||||
}
|
||||
if (v == null && dynamicProp != null) {
|
||||
for (PropertySource propertySource : dynamicProp) {
|
||||
if (propertySource instanceof SecurePropertySource) {
|
||||
v = ((SecurePropertySource) propertySource).getProperty(n, classLoader);
|
||||
} else {
|
||||
v = propertySource.getProperty(n);
|
||||
}
|
||||
if (v != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v == null)
|
||||
v = "${" + n + "}";
|
||||
|
||||
sb.append(v);
|
||||
prev = endName + 1;
|
||||
}
|
||||
}
|
||||
if (prev < value.length())
|
||||
sb.append(value.substring(prev));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse of Introspector.decapitalize.
|
||||
* @param name The name
|
||||
* @return the capitalized string
|
||||
*/
|
||||
public static String capitalize(String name) {
|
||||
if (name == null || name.length() == 0) {
|
||||
return name;
|
||||
}
|
||||
char chars[] = name.toCharArray();
|
||||
chars[0] = Character.toUpperCase(chars[0]);
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
// -------------------- other utils --------------------
|
||||
public static void clear() {
|
||||
objectMethods.clear();
|
||||
}
|
||||
|
||||
private static final Hashtable<Class<?>,Method[]> objectMethods = new Hashtable<>();
|
||||
|
||||
public static Method[] findMethods(Class<?> c) {
|
||||
Method methods[] = objectMethods.get(c);
|
||||
if (methods != null)
|
||||
return methods;
|
||||
|
||||
methods = c.getMethods();
|
||||
objectMethods.put(c, methods);
|
||||
return methods;
|
||||
}
|
||||
|
||||
@SuppressWarnings("null") // params cannot be null when comparing lengths
|
||||
public static Method findMethod(Class<?> c, String name,
|
||||
Class<?> params[]) {
|
||||
Method methods[] = findMethods(c);
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(name)) {
|
||||
Class<?> methodParams[] = methods[i].getParameterTypes();
|
||||
if (params == null && methodParams.length == 0) {
|
||||
return methods[i];
|
||||
}
|
||||
if (params.length != methodParams.length) {
|
||||
continue;
|
||||
}
|
||||
boolean found = true;
|
||||
for (int j = 0; j < params.length; j++) {
|
||||
if (params[j] != methodParams[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return methods[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Object callMethod1(Object target, String methodN,
|
||||
Object param1, String typeParam1, ClassLoader cl) throws Exception {
|
||||
if (target == null || param1 == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"IntrospectionUtils: Assert: Illegal params " +
|
||||
target + " " + param1);
|
||||
}
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: callMethod1 " +
|
||||
target.getClass().getName() + " " +
|
||||
param1.getClass().getName() + " " + typeParam1);
|
||||
|
||||
Class<?> params[] = new Class[1];
|
||||
if (typeParam1 == null)
|
||||
params[0] = param1.getClass();
|
||||
else
|
||||
params[0] = cl.loadClass(typeParam1);
|
||||
Method m = findMethod(target.getClass(), methodN, params);
|
||||
if (m == null)
|
||||
throw new NoSuchMethodException(target.getClass().getName() + " "
|
||||
+ methodN);
|
||||
try {
|
||||
return m.invoke(target, new Object[] { param1 });
|
||||
} catch (InvocationTargetException ie) {
|
||||
ExceptionUtils.handleThrowable(ie.getCause());
|
||||
throw ie;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object callMethodN(Object target, String methodN,
|
||||
Object params[], Class<?> typeParams[]) throws Exception {
|
||||
Method m = null;
|
||||
m = findMethod(target.getClass(), methodN, typeParams);
|
||||
if (m == null) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: Can't find method " + methodN +
|
||||
" in " + target + " CLASS " + target.getClass());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Object o = m.invoke(target, params);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
// debug
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(target.getClass().getName()).append('.')
|
||||
.append(methodN).append("( ");
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (i > 0)
|
||||
sb.append(", ");
|
||||
sb.append(params[i]);
|
||||
}
|
||||
sb.append(")");
|
||||
log.debug("IntrospectionUtils:" + sb.toString());
|
||||
}
|
||||
return o;
|
||||
} catch (InvocationTargetException ie) {
|
||||
ExceptionUtils.handleThrowable(ie.getCause());
|
||||
throw ie;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object convert(String object, Class<?> paramType) {
|
||||
Object result = null;
|
||||
if ("java.lang.String".equals(paramType.getName())) {
|
||||
result = object;
|
||||
} else if ("java.lang.Integer".equals(paramType.getName())
|
||||
|| "int".equals(paramType.getName())) {
|
||||
try {
|
||||
result = Integer.valueOf(object);
|
||||
} catch (NumberFormatException ex) {
|
||||
}
|
||||
// Try a setFoo ( boolean )
|
||||
} else if ("java.lang.Boolean".equals(paramType.getName())
|
||||
|| "boolean".equals(paramType.getName())) {
|
||||
result = Boolean.valueOf(object);
|
||||
|
||||
// Try a setFoo ( InetAddress )
|
||||
} else if ("java.net.InetAddress".equals(paramType
|
||||
.getName())) {
|
||||
try {
|
||||
result = InetAddress.getByName(object);
|
||||
} catch (UnknownHostException exc) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: Unable to resolve host name:" +
|
||||
object);
|
||||
}
|
||||
|
||||
// Unknown type
|
||||
} else {
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("IntrospectionUtils: Unknown type " +
|
||||
paramType.getName());
|
||||
}
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("Can't convert argument: " + object);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see if the specified class is an instance of or assignable from
|
||||
* the specified type. The class <code>clazz</code>, all its superclasses,
|
||||
* interfaces and those superinterfaces are tested for a match against
|
||||
* the type name <code>type</code>.
|
||||
*
|
||||
* This is similar to <code>instanceof</code> or {@link Class#isAssignableFrom}
|
||||
* except that the target type will not be resolved into a Class
|
||||
* object, which provides some security and memory benefits.
|
||||
*
|
||||
* @param clazz The class to test for a match.
|
||||
* @param type The name of the type that <code>clazz</code> must be.
|
||||
*
|
||||
* @return <code>true</code> if the <code>clazz</code> tested is an
|
||||
* instance of the specified <code>type</code>,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public static boolean isInstance(Class<?> clazz, String type) {
|
||||
if (type.equals(clazz.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Class<?>[] ifaces = clazz.getInterfaces();
|
||||
for (Class<?> iface : ifaces) {
|
||||
if (isInstance(iface, type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> superClazz = clazz.getSuperclass();
|
||||
if (superClazz == null) {
|
||||
return false;
|
||||
} else {
|
||||
return isInstance(superClazz, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Get property --------------------
|
||||
// This provides a layer of abstraction
|
||||
|
||||
public static interface PropertySource {
|
||||
public String getProperty(String key);
|
||||
}
|
||||
|
||||
|
||||
public static interface SecurePropertySource extends PropertySource {
|
||||
|
||||
/**
|
||||
* Obtain a property value, checking that code associated with the
|
||||
* provided class loader has permission to access the property. If the
|
||||
* {@code classLoader} is {@code null} or if {@code classLoader} does
|
||||
* not implement {@link PermissionCheck} then the property value will be
|
||||
* looked up <b>without</b> a call to
|
||||
* {@link PermissionCheck#check(java.security.Permission)}
|
||||
*
|
||||
* @param key The key of the requested property
|
||||
* @param classLoader The class loader associated with the code that
|
||||
* trigger the property lookup
|
||||
* @return The property value or {@code null} if it could not be found
|
||||
* or if {@link PermissionCheck#check(java.security.Permission)}
|
||||
* fails
|
||||
*/
|
||||
public String getProperty(String key, ClassLoader classLoader);
|
||||
}
|
||||
}
|
||||
30
java/org/apache/tomcat/util/LocalStrings.properties
Normal file
30
java/org/apache/tomcat/util/LocalStrings.properties
Normal file
@@ -0,0 +1,30 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=Full thread dump
|
||||
diagnostics.vmInfoClassCompilation=Class compilation
|
||||
diagnostics.vmInfoClassLoading=Class loading
|
||||
diagnostics.vmInfoGarbageCollectors=Garbage Collector [{0}]
|
||||
diagnostics.vmInfoLogger=Logger information
|
||||
diagnostics.vmInfoMemory=Memory information
|
||||
diagnostics.vmInfoMemoryManagers=Memory Manager [{0}]
|
||||
diagnostics.vmInfoMemoryPools=Memory Pool [{0}]
|
||||
diagnostics.vmInfoOs=OS information
|
||||
diagnostics.vmInfoPath=Path information
|
||||
diagnostics.vmInfoRuntime=Runtime information
|
||||
diagnostics.vmInfoStartup=Startup arguments
|
||||
diagnostics.vmInfoSystem=System properties
|
||||
diagnostics.vmInfoThreadCounts=Thread counts
|
||||
diagnostics.vmInfoThreadMxBean=ThreadMXBean capabilities
|
||||
18
java/org/apache/tomcat/util/LocalStrings_de.properties
Normal file
18
java/org/apache/tomcat/util/LocalStrings_de.properties
Normal file
@@ -0,0 +1,18 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=Kompletter Thread Dump
|
||||
diagnostics.vmInfoClassCompilation=Übersetzung der Klasse
|
||||
diagnostics.vmInfoLogger=Logger-Information
|
||||
19
java/org/apache/tomcat/util/LocalStrings_es.properties
Normal file
19
java/org/apache/tomcat/util/LocalStrings_es.properties
Normal file
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=Volcado del hilo completo
|
||||
diagnostics.vmInfoClassCompilation=Compilación de clase
|
||||
diagnostics.vmInfoLogger=Información de logueo
|
||||
diagnostics.vmInfoSystem=Propiedades del sistema
|
||||
30
java/org/apache/tomcat/util/LocalStrings_fr.properties
Normal file
30
java/org/apache/tomcat/util/LocalStrings_fr.properties
Normal file
@@ -0,0 +1,30 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=Traces complètes des threads
|
||||
diagnostics.vmInfoClassCompilation=Compilation de la classe
|
||||
diagnostics.vmInfoClassLoading=Chargeur de classes
|
||||
diagnostics.vmInfoGarbageCollectors=Garbage Collector [{0}]
|
||||
diagnostics.vmInfoLogger=Information sur le journal
|
||||
diagnostics.vmInfoMemory=Information mémoire
|
||||
diagnostics.vmInfoMemoryManagers=Gestionnaire de mémoire [{0}]
|
||||
diagnostics.vmInfoMemoryPools=Pool de mémoire [{0}]
|
||||
diagnostics.vmInfoOs=Information sur l'OS
|
||||
diagnostics.vmInfoPath=Imformation de chemin
|
||||
diagnostics.vmInfoRuntime=Information sur l'environnement d'exécution
|
||||
diagnostics.vmInfoStartup=Arguments de démarrage
|
||||
diagnostics.vmInfoSystem=Paramètres système
|
||||
diagnostics.vmInfoThreadCounts=Nombre de fils d'exécution (threads)
|
||||
diagnostics.vmInfoThreadMxBean=Capacités de ThreadMXBean
|
||||
30
java/org/apache/tomcat/util/LocalStrings_ja.properties
Normal file
30
java/org/apache/tomcat/util/LocalStrings_ja.properties
Normal file
@@ -0,0 +1,30 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=完全なスレッドダンプ
|
||||
diagnostics.vmInfoClassCompilation=Class コンパイル
|
||||
diagnostics.vmInfoClassLoading=クラスローディング
|
||||
diagnostics.vmInfoGarbageCollectors=ガベージコレクタ [{0}]
|
||||
diagnostics.vmInfoLogger=Logger 情報
|
||||
diagnostics.vmInfoMemory=メモリ情報
|
||||
diagnostics.vmInfoMemoryManagers=メモリマネージャ[{0}]
|
||||
diagnostics.vmInfoMemoryPools=メモリプール [{0}]
|
||||
diagnostics.vmInfoOs=OS情報
|
||||
diagnostics.vmInfoPath=パス情報
|
||||
diagnostics.vmInfoRuntime=Runtime 情報
|
||||
diagnostics.vmInfoStartup=起動引数
|
||||
diagnostics.vmInfoSystem=システムプロパティ
|
||||
diagnostics.vmInfoThreadCounts=スレッドカウント
|
||||
diagnostics.vmInfoThreadMxBean=ThreadMXBeanの機能
|
||||
30
java/org/apache/tomcat/util/LocalStrings_ko.properties
Normal file
30
java/org/apache/tomcat/util/LocalStrings_ko.properties
Normal file
@@ -0,0 +1,30 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=풀 쓰레드 덤프
|
||||
diagnostics.vmInfoClassCompilation=클래스 컴파일
|
||||
diagnostics.vmInfoClassLoading=클래스 로딩
|
||||
diagnostics.vmInfoGarbageCollectors=Garbage Collector [{0}]
|
||||
diagnostics.vmInfoLogger=Logger 정보
|
||||
diagnostics.vmInfoMemory=메모리 정보
|
||||
diagnostics.vmInfoMemoryManagers=메모리 매니저 [{0}]
|
||||
diagnostics.vmInfoMemoryPools=메모리 풀 [{0}]
|
||||
diagnostics.vmInfoOs=운영체제 정보
|
||||
diagnostics.vmInfoPath=경로 정보
|
||||
diagnostics.vmInfoRuntime=런타임 정보
|
||||
diagnostics.vmInfoStartup=프로그램 시작 아규먼트들
|
||||
diagnostics.vmInfoSystem=시스템 프로퍼티들
|
||||
diagnostics.vmInfoThreadCounts=쓰레드 개수
|
||||
diagnostics.vmInfoThreadMxBean=ThreadMXBean 용량정보들
|
||||
16
java/org/apache/tomcat/util/LocalStrings_ru.properties
Normal file
16
java/org/apache/tomcat/util/LocalStrings_ru.properties
Normal file
@@ -0,0 +1,16 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=Сброс полной нити
|
||||
24
java/org/apache/tomcat/util/LocalStrings_zh_CN.properties
Normal file
24
java/org/apache/tomcat/util/LocalStrings_zh_CN.properties
Normal file
@@ -0,0 +1,24 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
diagnostics.threadDumpTitle=打印全部线程
|
||||
diagnostics.vmInfoClassCompilation=:)class汇编
|
||||
diagnostics.vmInfoClassLoading=类加载中
|
||||
diagnostics.vmInfoGarbageCollectors=垃圾收集器[{0}]
|
||||
diagnostics.vmInfoLogger=日志记录器(Logger)信息
|
||||
diagnostics.vmInfoOs=操作系统信息
|
||||
diagnostics.vmInfoRuntime=运行时信息
|
||||
diagnostics.vmInfoSystem=系统.属性
|
||||
diagnostics.vmInfoThreadCounts=线程数
|
||||
99
java/org/apache/tomcat/util/MultiThrowable.java
Normal file
99
java/org/apache/tomcat/util/MultiThrowable.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wraps a list of throwables as a single throwable. This is intended to be used
|
||||
* when multiple actions are taken where each may throw an exception but all
|
||||
* actions are taken before any errors are reported.
|
||||
* <p>
|
||||
* This class is <b>NOT</b> threadsafe.
|
||||
*/
|
||||
public class MultiThrowable extends Throwable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private List<Throwable> throwables = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Add a throwable to the list of wrapped throwables.
|
||||
*
|
||||
* @param t The throwable to add
|
||||
*/
|
||||
public void add(Throwable t) {
|
||||
throwables.add(t);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return A read-only list of the wrapped throwables.
|
||||
*/
|
||||
public List<Throwable> getThrowables() {
|
||||
return Collections.unmodifiableList(throwables);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {@code null} if there are no wrapped throwables, the Throwable if
|
||||
* there is a single wrapped throwable or the current instance of
|
||||
* there are multiple wrapped throwables
|
||||
*/
|
||||
public Throwable getThrowable() {
|
||||
if (size() == 0) {
|
||||
return null;
|
||||
} else if (size() == 1) {
|
||||
return throwables.get(0);
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return The number of throwables currently wrapped by this instance.
|
||||
*/
|
||||
public int size() {
|
||||
return throwables.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overrides the default implementation to provide a concatenation of the
|
||||
* messages associated with each of the wrapped throwables. Note that the
|
||||
* format of the returned String is not guaranteed to be fixed and may
|
||||
* change in a future release.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(": ");
|
||||
sb.append(size());
|
||||
sb.append(" wrapped Throwables: ");
|
||||
for (Throwable t : throwables) {
|
||||
sb.append("[");
|
||||
sb.append(t.getMessage());
|
||||
sb.append("]");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
190
java/org/apache/tomcat/util/bcel/Const.java
Normal file
190
java/org/apache/tomcat/util/bcel/Const.java
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel;
|
||||
|
||||
/**
|
||||
* Constants for the project, mostly defined in the JVM specification.
|
||||
*/
|
||||
public final class Const {
|
||||
|
||||
/** One of the access flags for fields, methods, or classes.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1">
|
||||
* Flag definitions for Classes in the Java Virtual Machine Specification (Java SE 9 Edition).</a>
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5">
|
||||
* Flag definitions for Fields in the Java Virtual Machine Specification (Java SE 9 Edition).</a>
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6">
|
||||
* Flag definitions for Methods in the Java Virtual Machine Specification (Java SE 9 Edition).</a>
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1">
|
||||
* Flag definitions for Inner Classes in the Java Virtual Machine Specification (Java SE 9 Edition).</a>
|
||||
*/
|
||||
public static final short ACC_FINAL = 0x0010;
|
||||
|
||||
/** One of the access flags for fields, methods, or classes.
|
||||
*/
|
||||
public static final short ACC_INTERFACE = 0x0200;
|
||||
|
||||
/** One of the access flags for fields, methods, or classes.
|
||||
*/
|
||||
public static final short ACC_ABSTRACT = 0x0400;
|
||||
|
||||
/** One of the access flags for fields, methods, or classes.
|
||||
*/
|
||||
public static final short ACC_ANNOTATION = 0x2000;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as type UTF-8.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.7">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Utf8 = 1;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as type Integer.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.4">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Integer = 3;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as type Float.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.4">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Float = 4;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as type Long.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.5">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Long = 5;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as type Double.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.5">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Double = 6;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Class
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.1">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Class = 7;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as type String
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.3">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_String = 8;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Field Reference.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.2">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Fieldref = 9;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Method Reference.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.2">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Methodref = 10;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as an Interface Method Reference.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.2">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_InterfaceMethodref = 11;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a name and type.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.6">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_NameAndType = 12;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Method Handle.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.8">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_MethodHandle = 15;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Method Type.
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.9">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_MethodType = 16;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as dynamically computed.
|
||||
* @see <a href="https://bugs.openjdk.java.net/secure/attachment/74618/constant-dynamic.html">
|
||||
* Change request for JEP 309</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Dynamic = 17;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as an Invoke Dynamic
|
||||
* @see <a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.10">
|
||||
* The Constant Pool in The Java Virtual Machine Specification</a>
|
||||
*/
|
||||
public static final byte CONSTANT_InvokeDynamic = 18;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Module Reference.
|
||||
*
|
||||
* <p>Note: Early access Java 9 support- currently subject to change</p>
|
||||
*
|
||||
* @see <a href="http://cr.openjdk.java.net/~mr/jigsaw/spec/lang-vm.html#jigsaw-2.6">
|
||||
* JPMS: Modules in the Java Language and JVM</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Module = 19;
|
||||
|
||||
/**
|
||||
* Marks a constant pool entry as a Package Reference.
|
||||
*
|
||||
* <p>Note: Early access Java 9 support- currently subject to change</p>
|
||||
*
|
||||
* @see <a href="http://cr.openjdk.java.net/~mr/jigsaw/spec/lang-vm.html#jigsaw-2.6">
|
||||
* JPMS: Modules in the Java Language and JVM</a>
|
||||
*/
|
||||
public static final byte CONSTANT_Package = 20;
|
||||
|
||||
/**
|
||||
* The names of the types of entries in a constant pool.
|
||||
* Use getConstantName instead
|
||||
*/
|
||||
private static final String[] CONSTANT_NAMES = {
|
||||
"", "CONSTANT_Utf8", "", "CONSTANT_Integer",
|
||||
"CONSTANT_Float", "CONSTANT_Long", "CONSTANT_Double",
|
||||
"CONSTANT_Class", "CONSTANT_String", "CONSTANT_Fieldref",
|
||||
"CONSTANT_Methodref", "CONSTANT_InterfaceMethodref",
|
||||
"CONSTANT_NameAndType", "", "", "CONSTANT_MethodHandle",
|
||||
"CONSTANT_MethodType", "CONSTANT_Dynamic", "CONSTANT_InvokeDynamic",
|
||||
"CONSTANT_Module", "CONSTANT_Package"};
|
||||
|
||||
public static String getConstantName(int index) {
|
||||
return CONSTANT_NAMES[index];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
public class AnnotationElementValue extends ElementValue
|
||||
{
|
||||
// For annotation element values, this is the annotation
|
||||
private final AnnotationEntry annotationEntry;
|
||||
|
||||
AnnotationElementValue(final int type, final AnnotationEntry annotationEntry,
|
||||
final ConstantPool cpool)
|
||||
{
|
||||
super(type, cpool);
|
||||
if (type != ANNOTATION) {
|
||||
throw new RuntimeException(
|
||||
"Only element values of type annotation can be built with this ctor - type specified: " + type);
|
||||
}
|
||||
this.annotationEntry = annotationEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stringifyValue()
|
||||
{
|
||||
return annotationEntry.toString();
|
||||
}
|
||||
|
||||
public AnnotationEntry getAnnotationEntry()
|
||||
{
|
||||
return annotationEntry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* represents one annotation in the annotation table
|
||||
*/
|
||||
public class AnnotationEntry {
|
||||
|
||||
private final int type_index;
|
||||
private final ConstantPool constant_pool;
|
||||
|
||||
private final List<ElementValuePair> element_value_pairs;
|
||||
|
||||
/*
|
||||
* Creates an AnnotationEntry from a DataInputStream
|
||||
*
|
||||
* @param input
|
||||
* @param constant_pool
|
||||
* @throws IOException
|
||||
*/
|
||||
AnnotationEntry(final DataInput input, final ConstantPool constant_pool) throws IOException {
|
||||
|
||||
this.constant_pool = constant_pool;
|
||||
|
||||
type_index = input.readUnsignedShort();
|
||||
final int num_element_value_pairs = input.readUnsignedShort();
|
||||
|
||||
element_value_pairs = new ArrayList<>(num_element_value_pairs);
|
||||
for (int i = 0; i < num_element_value_pairs; i++) {
|
||||
element_value_pairs.add(new ElementValuePair(input, constant_pool));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the annotation type name
|
||||
*/
|
||||
public String getAnnotationType() {
|
||||
final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(type_index, Const.CONSTANT_Utf8);
|
||||
return c.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the element value pairs in this annotation entry
|
||||
*/
|
||||
public List<ElementValuePair> getElementValuePairs() {
|
||||
return element_value_pairs;
|
||||
}
|
||||
}
|
||||
49
java/org/apache/tomcat/util/bcel/classfile/Annotations.java
Normal file
49
java/org/apache/tomcat/util/bcel/classfile/Annotations.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* base class for annotations
|
||||
*/
|
||||
public class Annotations {
|
||||
|
||||
private final AnnotationEntry[] annotation_table;
|
||||
|
||||
/**
|
||||
* @param input Input stream
|
||||
* @param constant_pool Array of constants
|
||||
*/
|
||||
Annotations(final DataInput input, final ConstantPool constant_pool) throws IOException {
|
||||
final int annotation_table_length = input.readUnsignedShort();
|
||||
annotation_table = new AnnotationEntry[annotation_table_length];
|
||||
for (int i = 0; i < annotation_table_length; i++) {
|
||||
annotation_table[i] = new AnnotationEntry(input, constant_pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the array of annotation entries in this annotation
|
||||
*/
|
||||
public AnnotationEntry[] getAnnotationEntries() {
|
||||
return annotation_table;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
public class ArrayElementValue extends ElementValue
|
||||
{
|
||||
// For array types, this is the array
|
||||
private final ElementValue[] evalues;
|
||||
|
||||
ArrayElementValue(final int type, final ElementValue[] datums, final ConstantPool cpool)
|
||||
{
|
||||
super(type, cpool);
|
||||
if (type != ARRAY) {
|
||||
throw new RuntimeException(
|
||||
"Only element values of type array can be built with this ctor - type specified: " + type);
|
||||
}
|
||||
this.evalues = datums;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stringifyValue()
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
for (int i = 0; i < evalues.length; i++)
|
||||
{
|
||||
sb.append(evalues[i].stringifyValue());
|
||||
if ((i + 1) < evalues.length) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public ElementValue[] getElementValuesArray()
|
||||
{
|
||||
return evalues;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
public class ClassElementValue extends ElementValue
|
||||
{
|
||||
// For primitive types and string type, this points to the value entry in
|
||||
// the cpool
|
||||
// For 'class' this points to the class entry in the cpool
|
||||
private final int idx;
|
||||
|
||||
ClassElementValue(final int type, final int idx, final ConstantPool cpool) {
|
||||
super(type, cpool);
|
||||
this.idx = idx;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String stringifyValue()
|
||||
{
|
||||
final ConstantUtf8 cu8 = (ConstantUtf8) super.getConstantPool().getConstant(idx,
|
||||
Const.CONSTANT_Utf8);
|
||||
return cu8.getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
/**
|
||||
* Thrown when the BCEL attempts to read a class file and determines
|
||||
* that the file is malformed or otherwise cannot be interpreted as a
|
||||
* class file.
|
||||
*/
|
||||
public class ClassFormatException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 3243149520175287759L;
|
||||
|
||||
public ClassFormatException() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public ClassFormatException(final String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
247
java/org/apache/tomcat/util/bcel/classfile/ClassParser.java
Normal file
247
java/org/apache/tomcat/util/bcel/classfile/ClassParser.java
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* Wrapper class that parses a given Java .class file. The method <A
|
||||
* href ="#parse">parse</A> returns a <A href ="JavaClass.html">
|
||||
* JavaClass</A> object on success. When an I/O error or an
|
||||
* inconsistency occurs an appropriate exception is propagated back to
|
||||
* the caller.
|
||||
*
|
||||
* The structure and the names comply, except for a few conveniences,
|
||||
* exactly with the <A href="http://docs.oracle.com/javase/specs/">
|
||||
* JVM specification 1.0</a>. See this paper for
|
||||
* further details about the structure of a bytecode file.
|
||||
*/
|
||||
public final class ClassParser {
|
||||
|
||||
private static final int MAGIC = 0xCAFEBABE;
|
||||
|
||||
private final DataInput dataInputStream;
|
||||
private String class_name, superclass_name;
|
||||
private int access_flags; // Access rights of parsed class
|
||||
private String[] interface_names; // Names of implemented interfaces
|
||||
private ConstantPool constant_pool; // collection of constants
|
||||
private Annotations runtimeVisibleAnnotations; // "RuntimeVisibleAnnotations" attribute defined in the class
|
||||
private static final int BUFSIZE = 8192;
|
||||
|
||||
private static final String[] INTERFACES_EMPTY_ARRAY = new String[0];
|
||||
|
||||
/**
|
||||
* Parses class from the given stream.
|
||||
*
|
||||
* @param inputStream Input stream
|
||||
*/
|
||||
public ClassParser(final InputStream inputStream) {
|
||||
this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given Java class file and return an object that represents
|
||||
* the contained data, i.e., constants, methods, fields and commands.
|
||||
* A <em>ClassFormatException</em> is raised, if the file is not a valid
|
||||
* .class file. (This does not include verification of the byte code as it
|
||||
* is performed by the java interpreter).
|
||||
*
|
||||
* @return Class object representing the parsed class file
|
||||
* @throws IOException If an I/O occurs reading the byte code
|
||||
* @throws ClassFormatException If the byte code is invalid
|
||||
*/
|
||||
public JavaClass parse() throws IOException, ClassFormatException {
|
||||
/****************** Read headers ********************************/
|
||||
// Check magic tag of class file
|
||||
readID();
|
||||
// Get compiler version
|
||||
readVersion();
|
||||
/****************** Read constant pool and related **************/
|
||||
// Read constant pool entries
|
||||
readConstantPool();
|
||||
// Get class information
|
||||
readClassInfo();
|
||||
// Get interface information, i.e., implemented interfaces
|
||||
readInterfaces();
|
||||
/****************** Read class fields and methods ***************/
|
||||
// Read class fields, i.e., the variables of the class
|
||||
readFields();
|
||||
// Read class methods, i.e., the functions in the class
|
||||
readMethods();
|
||||
// Read class attributes
|
||||
readAttributes();
|
||||
|
||||
// Return the information we have gathered in a new object
|
||||
return new JavaClass(class_name, superclass_name,
|
||||
access_flags, constant_pool, interface_names,
|
||||
runtimeVisibleAnnotations);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads information about the attributes of the class.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readAttributes() throws IOException, ClassFormatException {
|
||||
final int attributes_count = dataInputStream.readUnsignedShort();
|
||||
for (int i = 0; i < attributes_count; i++) {
|
||||
ConstantUtf8 c;
|
||||
String name;
|
||||
int name_index;
|
||||
int length;
|
||||
// Get class name from constant pool via `name_index' indirection
|
||||
name_index = dataInputStream.readUnsignedShort();
|
||||
c = (ConstantUtf8) constant_pool.getConstant(name_index,
|
||||
Const.CONSTANT_Utf8);
|
||||
name = c.getBytes();
|
||||
// Length of data in bytes
|
||||
length = dataInputStream.readInt();
|
||||
|
||||
if (name.equals("RuntimeVisibleAnnotations")) {
|
||||
if (runtimeVisibleAnnotations != null) {
|
||||
throw new ClassFormatException(
|
||||
"RuntimeVisibleAnnotations attribute is not allowed more than once in a class file");
|
||||
}
|
||||
runtimeVisibleAnnotations = new Annotations(dataInputStream, constant_pool);
|
||||
} else {
|
||||
// All other attributes are skipped
|
||||
Utility.skipFully(dataInputStream, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads information about the class and its super class.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readClassInfo() throws IOException, ClassFormatException {
|
||||
access_flags = dataInputStream.readUnsignedShort();
|
||||
/* Interfaces are implicitly abstract, the flag should be set
|
||||
* according to the JVM specification.
|
||||
*/
|
||||
if ((access_flags & Const.ACC_INTERFACE) != 0) {
|
||||
access_flags |= Const.ACC_ABSTRACT;
|
||||
}
|
||||
if (((access_flags & Const.ACC_ABSTRACT) != 0)
|
||||
&& ((access_flags & Const.ACC_FINAL) != 0)) {
|
||||
throw new ClassFormatException("Class can't be both final and abstract");
|
||||
}
|
||||
|
||||
int class_name_index = dataInputStream.readUnsignedShort();
|
||||
class_name = Utility.getClassName(constant_pool, class_name_index);
|
||||
|
||||
int superclass_name_index = dataInputStream.readUnsignedShort();
|
||||
if (superclass_name_index > 0) {
|
||||
// May be zero -> class is java.lang.Object
|
||||
superclass_name = Utility.getClassName(constant_pool, superclass_name_index);
|
||||
} else {
|
||||
superclass_name = "java.lang.Object";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads constant pool entries.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readConstantPool() throws IOException, ClassFormatException {
|
||||
constant_pool = new ConstantPool(dataInputStream);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads information about the fields of the class, i.e., its variables.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readFields() throws IOException, ClassFormatException {
|
||||
final int fields_count = dataInputStream.readUnsignedShort();
|
||||
for (int i = 0; i < fields_count; i++) {
|
||||
Utility.swallowFieldOrMethod(dataInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************** Private utility methods **********************/
|
||||
/**
|
||||
* Checks whether the header of the file is ok.
|
||||
* Of course, this has to be the first action on successive file reads.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readID() throws IOException, ClassFormatException {
|
||||
if (dataInputStream.readInt() != MAGIC) {
|
||||
throw new ClassFormatException("It is not a Java .class file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads information about the interfaces implemented by this class.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readInterfaces() throws IOException, ClassFormatException {
|
||||
final int interfaces_count = dataInputStream.readUnsignedShort();
|
||||
if (interfaces_count > 0) {
|
||||
interface_names = new String[interfaces_count];
|
||||
for (int i = 0; i < interfaces_count; i++) {
|
||||
int index = dataInputStream.readUnsignedShort();
|
||||
interface_names[i] = Utility.getClassName(constant_pool, index);
|
||||
}
|
||||
} else {
|
||||
interface_names = INTERFACES_EMPTY_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads information about the methods of the class.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readMethods() throws IOException, ClassFormatException {
|
||||
final int methods_count = dataInputStream.readUnsignedShort();
|
||||
for (int i = 0; i < methods_count; i++) {
|
||||
Utility.swallowFieldOrMethod(dataInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads major and minor version of compiler which created the file.
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
private void readVersion() throws IOException, ClassFormatException {
|
||||
// file.readUnsignedShort(); // Unused minor
|
||||
// file.readUnsignedShort(); // Unused major
|
||||
Utility.skipFully(dataInputStream, 4);
|
||||
}
|
||||
}
|
||||
108
java/org/apache/tomcat/util/bcel/classfile/Constant.java
Normal file
108
java/org/apache/tomcat/util/bcel/classfile/Constant.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* Abstract superclass for classes to represent the different constant types
|
||||
* in the constant pool of a class file. The classes keep closely to
|
||||
* the JVM specification.
|
||||
*
|
||||
* @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
|
||||
*/
|
||||
public abstract class Constant {
|
||||
|
||||
/* In fact this tag is redundant since we can distinguish different
|
||||
* `Constant' objects by their type, i.e., via `instanceof'. In some
|
||||
* places we will use the tag for switch()es anyway.
|
||||
*
|
||||
* First, we want match the specification as closely as possible. Second we
|
||||
* need the tag as an index to select the corresponding class name from the
|
||||
* `CONSTANT_NAMES' array.
|
||||
*/
|
||||
protected final byte tag;
|
||||
|
||||
Constant(final byte tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Tag of constant, i.e., its type. No setTag() method to avoid
|
||||
* confusion.
|
||||
*/
|
||||
public final byte getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one constant from the given input, the type depends on a tag byte.
|
||||
*
|
||||
* @param dataInput Input stream
|
||||
* @return Constant object
|
||||
* @throws IOException if an I/O error occurs reading from the given {@code dataInput}.
|
||||
* @throws ClassFormatException if the next byte is not recognized
|
||||
*/
|
||||
static Constant readConstant(final DataInput dataInput) throws IOException, ClassFormatException {
|
||||
final byte b = dataInput.readByte(); // Read tag byte
|
||||
int skipSize;
|
||||
switch (b) {
|
||||
case Const.CONSTANT_Class:
|
||||
return new ConstantClass(dataInput);
|
||||
case Const.CONSTANT_Integer:
|
||||
return new ConstantInteger(dataInput);
|
||||
case Const.CONSTANT_Float:
|
||||
return new ConstantFloat(dataInput);
|
||||
case Const.CONSTANT_Long:
|
||||
return new ConstantLong(dataInput);
|
||||
case Const.CONSTANT_Double:
|
||||
return new ConstantDouble(dataInput);
|
||||
case Const.CONSTANT_Utf8:
|
||||
return ConstantUtf8.getInstance(dataInput);
|
||||
case Const.CONSTANT_String:
|
||||
case Const.CONSTANT_MethodType:
|
||||
case Const.CONSTANT_Module:
|
||||
case Const.CONSTANT_Package:
|
||||
skipSize = 2; // unsigned short
|
||||
break;
|
||||
case Const.CONSTANT_MethodHandle:
|
||||
skipSize = 3; // unsigned byte, unsigned short
|
||||
break;
|
||||
case Const.CONSTANT_Fieldref:
|
||||
case Const.CONSTANT_Methodref:
|
||||
case Const.CONSTANT_InterfaceMethodref:
|
||||
case Const.CONSTANT_NameAndType:
|
||||
case Const.CONSTANT_Dynamic:
|
||||
case Const.CONSTANT_InvokeDynamic:
|
||||
skipSize = 4; // unsigned short, unsigned short
|
||||
break;
|
||||
default:
|
||||
throw new ClassFormatException("Invalid byte tag in constant pool: " + b);
|
||||
}
|
||||
Utility.skipFully(dataInput, skipSize);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + tag + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class is derived from the abstract {@link Constant}
|
||||
* and represents a reference to a (external) class.
|
||||
*
|
||||
* @see Constant
|
||||
*/
|
||||
public final class ConstantClass extends Constant {
|
||||
|
||||
private final int name_index; // Identical to ConstantString except for the name
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an instance from file data.
|
||||
*
|
||||
* @param dataInput Input stream
|
||||
* @throws IOException if an I/O error occurs reading from the given {@code dataInput}.
|
||||
*/
|
||||
ConstantClass(final DataInput dataInput) throws IOException {
|
||||
super(Const.CONSTANT_Class);
|
||||
this.name_index = dataInput.readUnsignedShort();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Name index in constant pool of class name.
|
||||
*/
|
||||
public int getNameIndex() {
|
||||
return name_index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class is derived from the abstract {@link Constant}
|
||||
* and represents a reference to a Double object.
|
||||
*
|
||||
* @see Constant
|
||||
*/
|
||||
public final class ConstantDouble extends Constant {
|
||||
|
||||
private final double bytes;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize instance from file data.
|
||||
*
|
||||
* @param file Input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
ConstantDouble(final DataInput file) throws IOException {
|
||||
super(Const.CONSTANT_Double);
|
||||
this.bytes = file.readDouble();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return data, i.e., 8 bytes.
|
||||
*/
|
||||
public double getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class is derived from the abstract {@link Constant}
|
||||
* and represents a reference to a float object.
|
||||
*
|
||||
* @see Constant
|
||||
*/
|
||||
public final class ConstantFloat extends Constant {
|
||||
|
||||
private final float bytes;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize instance from file data.
|
||||
*
|
||||
* @param file Input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
ConstantFloat(final DataInput file) throws IOException {
|
||||
super(Const.CONSTANT_Float);
|
||||
this.bytes = file.readFloat();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return data, i.e., 4 bytes.
|
||||
*/
|
||||
public float getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class is derived from the abstract {@link Constant}
|
||||
* and represents a reference to an int object.
|
||||
*
|
||||
* @see Constant
|
||||
*/
|
||||
public final class ConstantInteger extends Constant {
|
||||
|
||||
private final int bytes;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize instance from file data.
|
||||
*
|
||||
* @param file Input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
ConstantInteger(final DataInput file) throws IOException {
|
||||
super(Const.CONSTANT_Integer);
|
||||
this.bytes = file.readInt();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return data, i.e., 4 bytes.
|
||||
*/
|
||||
public int getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
54
java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java
Normal file
54
java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class is derived from the abstract {@link Constant}
|
||||
* and represents a reference to a long object.
|
||||
*
|
||||
* @see Constant
|
||||
*/
|
||||
public final class ConstantLong extends Constant {
|
||||
|
||||
private final long bytes;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize instance from file data.
|
||||
*
|
||||
* @param input Input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
ConstantLong(final DataInput input) throws IOException {
|
||||
super(Const.CONSTANT_Long);
|
||||
this.bytes = input.readLong();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return data, i.e., 8 bytes.
|
||||
*/
|
||||
public long getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
107
java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java
Normal file
107
java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class represents the constant pool, i.e., a table of constants, of
|
||||
* a parsed classfile. It may contain null references, due to the JVM
|
||||
* specification that skips an entry after an 8-byte constant (double,
|
||||
* long) entry. Those interested in generating constant pools
|
||||
* programatically should see <a href="../generic/ConstantPoolGen.html">
|
||||
* ConstantPoolGen</a>.
|
||||
|
||||
* @see Constant
|
||||
*/
|
||||
public class ConstantPool {
|
||||
|
||||
private final Constant[] constant_pool;
|
||||
|
||||
/**
|
||||
* Reads constants from given input stream.
|
||||
*
|
||||
* @param input Input stream
|
||||
* @throws IOException
|
||||
* @throws ClassFormatException
|
||||
*/
|
||||
ConstantPool(final DataInput input) throws IOException, ClassFormatException {
|
||||
final int constant_pool_count = input.readUnsignedShort();
|
||||
constant_pool = new Constant[constant_pool_count];
|
||||
/* constant_pool[0] is unused by the compiler and may be used freely
|
||||
* by the implementation.
|
||||
*/
|
||||
for (int i = 1; i < constant_pool_count; i++) {
|
||||
constant_pool[i] = Constant.readConstant(input);
|
||||
/* Quote from the JVM specification:
|
||||
* "All eight byte constants take up two spots in the constant pool.
|
||||
* If this is the n'th byte in the constant pool, then the next item
|
||||
* will be numbered n+2"
|
||||
*
|
||||
* Thus we have to increment the index counter.
|
||||
*/
|
||||
if (constant_pool[i] != null) {
|
||||
byte tag = constant_pool[i].getTag();
|
||||
if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets constant from constant pool.
|
||||
*
|
||||
* @param index Index in constant pool
|
||||
* @return Constant value
|
||||
* @see Constant
|
||||
*/
|
||||
public Constant getConstant( final int index ) {
|
||||
if (index >= constant_pool.length || index < 0) {
|
||||
throw new ClassFormatException("Invalid constant pool reference: " + index
|
||||
+ ". Constant pool size is: " + constant_pool.length);
|
||||
}
|
||||
return constant_pool[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets constant from constant pool and check whether it has the
|
||||
* expected type.
|
||||
*
|
||||
* @param index Index in constant pool
|
||||
* @param tag Tag of expected constant, i.e., its type
|
||||
* @return Constant value
|
||||
* @see Constant
|
||||
* @throws ClassFormatException If the constant is not of the expected type
|
||||
*/
|
||||
public Constant getConstant( final int index, final byte tag ) throws ClassFormatException {
|
||||
Constant c;
|
||||
c = getConstant(index);
|
||||
if (c == null) {
|
||||
throw new ClassFormatException("Constant pool at index " + index + " is null.");
|
||||
}
|
||||
if (c.getTag() != tag) {
|
||||
throw new ClassFormatException("Expected class `" + Const.getConstantName(tag)
|
||||
+ "' at index " + index + " and got " + c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
56
java/org/apache/tomcat/util/bcel/classfile/ConstantUtf8.java
Normal file
56
java/org/apache/tomcat/util/bcel/classfile/ConstantUtf8.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* This class is derived from the abstract
|
||||
* <A HREF="org.apache.tomcat.util.bcel.classfile.Constant.html">Constant</A> class
|
||||
* and represents a reference to a Utf8 encoded string.
|
||||
*
|
||||
* @see Constant
|
||||
*/
|
||||
public final class ConstantUtf8 extends Constant {
|
||||
|
||||
private final String bytes;
|
||||
|
||||
static ConstantUtf8 getInstance(final DataInput input) throws IOException {
|
||||
return new ConstantUtf8(input.readUTF());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes Data
|
||||
*/
|
||||
private ConstantUtf8(final String bytes) {
|
||||
super(Const.CONSTANT_Utf8);
|
||||
if (bytes == null) {
|
||||
throw new IllegalArgumentException("bytes must not be null!");
|
||||
}
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data converted to string.
|
||||
*/
|
||||
public final String getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
99
java/org/apache/tomcat/util/bcel/classfile/ElementValue.java
Normal file
99
java/org/apache/tomcat/util/bcel/classfile/ElementValue.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class ElementValue
|
||||
{
|
||||
private final int type;
|
||||
|
||||
private final ConstantPool cpool;
|
||||
|
||||
|
||||
ElementValue(final int type, final ConstantPool cpool) {
|
||||
this.type = type;
|
||||
this.cpool = cpool;
|
||||
}
|
||||
|
||||
public abstract String stringifyValue();
|
||||
|
||||
public static final byte STRING = 's';
|
||||
public static final byte ENUM_CONSTANT = 'e';
|
||||
public static final byte CLASS = 'c';
|
||||
public static final byte ANNOTATION = '@';
|
||||
public static final byte ARRAY = '[';
|
||||
public static final byte PRIMITIVE_INT = 'I';
|
||||
public static final byte PRIMITIVE_BYTE = 'B';
|
||||
public static final byte PRIMITIVE_CHAR = 'C';
|
||||
public static final byte PRIMITIVE_DOUBLE = 'D';
|
||||
public static final byte PRIMITIVE_FLOAT = 'F';
|
||||
public static final byte PRIMITIVE_LONG = 'J';
|
||||
public static final byte PRIMITIVE_SHORT = 'S';
|
||||
public static final byte PRIMITIVE_BOOLEAN = 'Z';
|
||||
|
||||
public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException
|
||||
{
|
||||
final byte type = input.readByte();
|
||||
switch (type)
|
||||
{
|
||||
case PRIMITIVE_BYTE:
|
||||
case PRIMITIVE_CHAR:
|
||||
case PRIMITIVE_DOUBLE:
|
||||
case PRIMITIVE_FLOAT:
|
||||
case PRIMITIVE_INT:
|
||||
case PRIMITIVE_LONG:
|
||||
case PRIMITIVE_SHORT:
|
||||
case PRIMITIVE_BOOLEAN:
|
||||
case STRING:
|
||||
return new SimpleElementValue(type, input.readUnsignedShort(), cpool);
|
||||
|
||||
case ENUM_CONSTANT:
|
||||
input.readUnsignedShort(); // Unused type_index
|
||||
return new EnumElementValue(ENUM_CONSTANT, input.readUnsignedShort(), cpool);
|
||||
|
||||
case CLASS:
|
||||
return new ClassElementValue(CLASS, input.readUnsignedShort(), cpool);
|
||||
|
||||
case ANNOTATION:
|
||||
// TODO isRuntimeVisible
|
||||
return new AnnotationElementValue(ANNOTATION, new AnnotationEntry(input, cpool), cpool);
|
||||
|
||||
case ARRAY:
|
||||
final int numArrayVals = input.readUnsignedShort();
|
||||
final ElementValue[] evalues = new ElementValue[numArrayVals];
|
||||
for (int j = 0; j < numArrayVals; j++)
|
||||
{
|
||||
evalues[j] = ElementValue.readElementValue(input, cpool);
|
||||
}
|
||||
return new ArrayElementValue(ARRAY, evalues, cpool);
|
||||
|
||||
default:
|
||||
throw new ClassFormatException(
|
||||
"Unexpected element value kind in annotation: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
final ConstantPool getConstantPool() {
|
||||
return cpool;
|
||||
}
|
||||
|
||||
final int getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* an annotation's element value pair
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ElementValuePair
|
||||
{
|
||||
private final ElementValue elementValue;
|
||||
|
||||
private final ConstantPool constantPool;
|
||||
|
||||
private final int elementNameIndex;
|
||||
|
||||
ElementValuePair(final DataInput file, final ConstantPool constantPool) throws IOException {
|
||||
this.constantPool = constantPool;
|
||||
this.elementNameIndex = file.readUnsignedShort();
|
||||
this.elementValue = ElementValue.readElementValue(file, constantPool);
|
||||
}
|
||||
|
||||
public String getNameString()
|
||||
{
|
||||
final ConstantUtf8 c = (ConstantUtf8) constantPool.getConstant(
|
||||
elementNameIndex, Const.CONSTANT_Utf8);
|
||||
return c.getBytes();
|
||||
}
|
||||
|
||||
public final ElementValue getValue()
|
||||
{
|
||||
return elementValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
public class EnumElementValue extends ElementValue
|
||||
{
|
||||
private final int valueIdx;
|
||||
|
||||
EnumElementValue(final int type, final int valueIdx, final ConstantPool cpool) {
|
||||
super(type, cpool);
|
||||
if (type != ENUM_CONSTANT)
|
||||
throw new RuntimeException(
|
||||
"Only element values of type enum can be built with this ctor - type specified: " + type);
|
||||
this.valueIdx = valueIdx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stringifyValue()
|
||||
{
|
||||
final ConstantUtf8 cu8 = (ConstantUtf8) super.getConstantPool().getConstant(valueIdx,
|
||||
Const.CONSTANT_Utf8);
|
||||
return cu8.getBytes();
|
||||
}
|
||||
}
|
||||
102
java/org/apache/tomcat/util/bcel/classfile/JavaClass.java
Normal file
102
java/org/apache/tomcat/util/bcel/classfile/JavaClass.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
/**
|
||||
* Represents a Java class, i.e., the data structures, constant pool,
|
||||
* fields, methods and commands contained in a Java .class file.
|
||||
* See <a href="http://docs.oracle.com/javase/specs/">JVM specification</a> for details.
|
||||
* The intent of this class is to represent a parsed or otherwise existing
|
||||
* class file. Those interested in programatically generating classes
|
||||
* should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
|
||||
*/
|
||||
public class JavaClass {
|
||||
|
||||
private final int access_flags;
|
||||
private final String class_name;
|
||||
private final String superclass_name;
|
||||
private final String[] interface_names;
|
||||
private final Annotations runtimeVisibleAnnotations; // "RuntimeVisibleAnnotations" attribute defined in the class
|
||||
|
||||
/**
|
||||
* Constructor gets all contents as arguments.
|
||||
*
|
||||
* @param class_name Name of this class.
|
||||
* @param superclass_name Name of this class's superclass.
|
||||
* @param access_flags Access rights defined by bit flags
|
||||
* @param constant_pool Array of constants
|
||||
* @param interface_names Implemented interfaces
|
||||
* @param runtimeVisibleAnnotations "RuntimeVisibleAnnotations" attribute defined on the Class, or null
|
||||
*/
|
||||
JavaClass(final String class_name, final String superclass_name,
|
||||
final int access_flags, final ConstantPool constant_pool, final String[] interface_names,
|
||||
final Annotations runtimeVisibleAnnotations) {
|
||||
this.access_flags = access_flags;
|
||||
this.runtimeVisibleAnnotations = runtimeVisibleAnnotations;
|
||||
this.class_name = class_name;
|
||||
this.superclass_name = superclass_name;
|
||||
this.interface_names = interface_names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Access flags of the object aka. "modifiers".
|
||||
*/
|
||||
public final int getAccessFlags() {
|
||||
return access_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return annotations entries from "RuntimeVisibleAnnotations" attribute on
|
||||
* the class, if there is any.
|
||||
*
|
||||
* @return An array of entries or {@code null}
|
||||
*/
|
||||
public AnnotationEntry[] getAnnotationEntries() {
|
||||
if (runtimeVisibleAnnotations != null) {
|
||||
return runtimeVisibleAnnotations.getAnnotationEntries();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Class name.
|
||||
*/
|
||||
public String getClassName() {
|
||||
return class_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Names of implemented interfaces.
|
||||
*/
|
||||
public String[] getInterfaceNames() {
|
||||
return interface_names;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns the super class name of this class. In the case that this class is
|
||||
* java.lang.Object, it will return itself (java.lang.Object). This is probably incorrect
|
||||
* but isn't fixed at this time to not break existing clients.
|
||||
*
|
||||
* @return Superclass name.
|
||||
*/
|
||||
public String getSuperclassName() {
|
||||
return superclass_name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
public class SimpleElementValue extends ElementValue
|
||||
{
|
||||
private final int index;
|
||||
|
||||
SimpleElementValue(final int type, final int index, final ConstantPool cpool) {
|
||||
super(type, cpool);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value entry index in the cpool
|
||||
*/
|
||||
public int getIndex()
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
// Whatever kind of value it is, return it as a string
|
||||
@Override
|
||||
public String stringifyValue()
|
||||
{
|
||||
final ConstantPool cpool = super.getConstantPool();
|
||||
final int _type = super.getType();
|
||||
switch (_type)
|
||||
{
|
||||
case PRIMITIVE_INT:
|
||||
final ConstantInteger c = (ConstantInteger) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Integer);
|
||||
return Integer.toString(c.getBytes());
|
||||
case PRIMITIVE_LONG:
|
||||
final ConstantLong j = (ConstantLong) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Long);
|
||||
return Long.toString(j.getBytes());
|
||||
case PRIMITIVE_DOUBLE:
|
||||
final ConstantDouble d = (ConstantDouble) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Double);
|
||||
return Double.toString(d.getBytes());
|
||||
case PRIMITIVE_FLOAT:
|
||||
final ConstantFloat f = (ConstantFloat) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Float);
|
||||
return Float.toString(f.getBytes());
|
||||
case PRIMITIVE_SHORT:
|
||||
final ConstantInteger s = (ConstantInteger) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Integer);
|
||||
return Integer.toString(s.getBytes());
|
||||
case PRIMITIVE_BYTE:
|
||||
final ConstantInteger b = (ConstantInteger) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Integer);
|
||||
return Integer.toString(b.getBytes());
|
||||
case PRIMITIVE_CHAR:
|
||||
final ConstantInteger ch = (ConstantInteger) cpool.getConstant(
|
||||
getIndex(), Const.CONSTANT_Integer);
|
||||
return String.valueOf((char)ch.getBytes());
|
||||
case PRIMITIVE_BOOLEAN:
|
||||
final ConstantInteger bo = (ConstantInteger) cpool.getConstant(
|
||||
getIndex(), Const.CONSTANT_Integer);
|
||||
if (bo.getBytes() == 0) {
|
||||
return "false";
|
||||
}
|
||||
return "true";
|
||||
case STRING:
|
||||
final ConstantUtf8 cu8 = (ConstantUtf8) cpool.getConstant(getIndex(),
|
||||
Const.CONSTANT_Utf8);
|
||||
return cu8.getBytes();
|
||||
default:
|
||||
throw new RuntimeException("SimpleElementValue class does not know how to stringify type " + _type);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
java/org/apache/tomcat/util/bcel/classfile/Utility.java
Normal file
86
java/org/apache/tomcat/util/bcel/classfile/Utility.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.bcel.classfile;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tomcat.util.bcel.Const;
|
||||
|
||||
/**
|
||||
* Utility functions that do not really belong to any class in particular.
|
||||
*/
|
||||
final class Utility {
|
||||
|
||||
private Utility() {
|
||||
// Hide default constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
|
||||
* if the
|
||||
* class name starts with this string and the flag <em>chopit</em> is true.
|
||||
* Slashes <em>/</em> are converted to dots <em>.</em>.
|
||||
*
|
||||
* @param str The long class name
|
||||
* @return Compacted class name
|
||||
*/
|
||||
static String compactClassName(final String str) {
|
||||
return str.replace('/', '.'); // Is `/' on all systems, even DOS
|
||||
}
|
||||
|
||||
static String getClassName(final ConstantPool constant_pool, final int index) {
|
||||
Constant c = constant_pool.getConstant(index, Const.CONSTANT_Class);
|
||||
int i = ((ConstantClass) c).getNameIndex();
|
||||
|
||||
// Finally get the string from the constant pool
|
||||
c = constant_pool.getConstant(i, Const.CONSTANT_Utf8);
|
||||
String name = ((ConstantUtf8) c).getBytes();
|
||||
|
||||
return compactClassName(name);
|
||||
}
|
||||
|
||||
static void skipFully(final DataInput file, final int length) throws IOException {
|
||||
int total = file.skipBytes(length);
|
||||
if (total != length) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
static void swallowFieldOrMethod(final DataInput file)
|
||||
throws IOException {
|
||||
// file.readUnsignedShort(); // Unused access flags
|
||||
// file.readUnsignedShort(); // name index
|
||||
// file.readUnsignedShort(); // signature index
|
||||
skipFully(file, 6);
|
||||
|
||||
int attributes_count = file.readUnsignedShort();
|
||||
for (int i = 0; i < attributes_count; i++) {
|
||||
swallowAttribute(file);
|
||||
}
|
||||
}
|
||||
|
||||
static void swallowAttribute(final DataInput file)
|
||||
throws IOException {
|
||||
//file.readUnsignedShort(); // Unused name index
|
||||
skipFully(file, 2);
|
||||
// Length of data in bytes
|
||||
int length = file.readInt();
|
||||
skipFully(file, length);
|
||||
}
|
||||
}
|
||||
27
java/org/apache/tomcat/util/bcel/classfile/package.html
Normal file
27
java/org/apache/tomcat/util/bcel/classfile/package.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body bgcolor="white">
|
||||
<p>
|
||||
This package contains the classes that describe the structure of a
|
||||
Java class file and a class file parser.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
30
java/org/apache/tomcat/util/bcel/package.html
Normal file
30
java/org/apache/tomcat/util/bcel/package.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body bgcolor="white">
|
||||
<p>
|
||||
This package contains basic classes for the
|
||||
<a href="https://commons.apache.org/bcel/">Byte Code Engineering Library</a>
|
||||
and constants defined by the
|
||||
<a href="http://docs.oracle.com/javase/specs/">
|
||||
JVM specification</a>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
181
java/org/apache/tomcat/util/buf/AbstractChunk.java
Normal file
181
java/org/apache/tomcat/util/buf/AbstractChunk.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Base class for the *Chunk implementation to reduce duplication.
|
||||
*/
|
||||
public abstract class AbstractChunk implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/*
|
||||
* JVMs may limit the maximum array size to slightly less than
|
||||
* Integer.MAX_VALUE. On markt's desktop the limit is MAX_VALUE - 2.
|
||||
* Comments in the JRE source code for ArrayList and other classes indicate
|
||||
* that it may be as low as MAX_VALUE - 8 on some systems.
|
||||
*/
|
||||
public static final int ARRAY_MAX_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
private int hashCode = 0;
|
||||
protected boolean hasHashCode = false;
|
||||
|
||||
protected boolean isSet;
|
||||
|
||||
private int limit = -1;
|
||||
|
||||
protected int start;
|
||||
protected int end;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum amount of data in this buffer. If -1 or not set, the buffer will
|
||||
* grow to {{@link #ARRAY_MAX_SIZE}. Can be smaller than the current buffer
|
||||
* size ( which will not shrink ). When the limit is reached, the buffer
|
||||
* will be flushed (if out is set) or throw exception.
|
||||
*
|
||||
* @param limit The new limit
|
||||
*/
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
|
||||
protected int getLimitInternal() {
|
||||
if (limit > 0) {
|
||||
return limit;
|
||||
} else {
|
||||
return ARRAY_MAX_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the start position of the data in the buffer
|
||||
*/
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
public void setEnd(int i) {
|
||||
end = i;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Deprecate offset and use start
|
||||
|
||||
public int getOffset() {
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
public void setOffset(int off) {
|
||||
if (end < off) {
|
||||
end = off;
|
||||
}
|
||||
start = off;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the length of the data in the buffer
|
||||
*/
|
||||
public int getLength() {
|
||||
return end - start;
|
||||
}
|
||||
|
||||
|
||||
public boolean isNull() {
|
||||
if (end > 0) {
|
||||
return false;
|
||||
}
|
||||
return !isSet;
|
||||
}
|
||||
|
||||
|
||||
public int indexOf(String src, int srcOff, int srcLen, int myOff) {
|
||||
char first = src.charAt(srcOff);
|
||||
|
||||
// Look for first char
|
||||
int srcEnd = srcOff + srcLen;
|
||||
|
||||
mainLoop: for (int i = myOff + start; i <= (end - srcLen); i++) {
|
||||
if (getBufferElement(i) != first) {
|
||||
continue;
|
||||
}
|
||||
// found first char, now look for a match
|
||||
int myPos = i + 1;
|
||||
for (int srcPos = srcOff + 1; srcPos < srcEnd;) {
|
||||
if (getBufferElement(myPos++) != src.charAt(srcPos++)) {
|
||||
continue mainLoop;
|
||||
}
|
||||
}
|
||||
return i - start; // found it
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resets the chunk to an uninitialized state.
|
||||
*/
|
||||
public void recycle() {
|
||||
hasHashCode = false;
|
||||
isSet = false;
|
||||
start = 0;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hasHashCode) {
|
||||
return hashCode;
|
||||
}
|
||||
int code = 0;
|
||||
|
||||
code = hash();
|
||||
hashCode = code;
|
||||
hasHashCode = true;
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
public int hash() {
|
||||
int code = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
code = code * 37 + getBufferElement(i);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
protected abstract int getBufferElement(int index);
|
||||
}
|
||||
103
java/org/apache/tomcat/util/buf/Ascii.java
Normal file
103
java/org/apache/tomcat/util/buf/Ascii.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
/**
|
||||
* This class implements some basic ASCII character handling functions.
|
||||
*
|
||||
* @author dac@eng.sun.com
|
||||
* @author James Todd [gonzo@eng.sun.com]
|
||||
*/
|
||||
public final class Ascii {
|
||||
/*
|
||||
* Character translation tables.
|
||||
*/
|
||||
private static final byte[] toLower = new byte[256];
|
||||
|
||||
/*
|
||||
* Character type tables.
|
||||
*/
|
||||
private static final boolean[] isDigit = new boolean[256];
|
||||
|
||||
private static final long OVERFLOW_LIMIT = Long.MAX_VALUE / 10;
|
||||
|
||||
/*
|
||||
* Initialize character translation and type tables.
|
||||
*/
|
||||
static {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
toLower[i] = (byte)i;
|
||||
}
|
||||
|
||||
for (int lc = 'a'; lc <= 'z'; lc++) {
|
||||
int uc = lc + 'A' - 'a';
|
||||
|
||||
toLower[uc] = (byte)lc;
|
||||
}
|
||||
|
||||
for (int d = '0'; d <= '9'; d++) {
|
||||
isDigit[d] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lower case equivalent of the specified ASCII character.
|
||||
* @param c The char
|
||||
* @return the lower case equivalent char
|
||||
*/
|
||||
public static int toLower(int c) {
|
||||
return toLower[c & 0xff] & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the specified ASCII character is a digit.
|
||||
* @param c The char
|
||||
*/
|
||||
private static boolean isDigit(int c) {
|
||||
return isDigit[c & 0xff];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an unsigned long from the specified subarray of bytes.
|
||||
* @param b the bytes to parse
|
||||
* @param off the start offset of the bytes
|
||||
* @param len the length of the bytes
|
||||
* @return the long value
|
||||
* @exception NumberFormatException if the long format was invalid
|
||||
*/
|
||||
public static long parseLong(byte[] b, int off, int len)
|
||||
throws NumberFormatException
|
||||
{
|
||||
int c;
|
||||
|
||||
if (b == null || len <= 0 || !isDigit(c = b[off++])) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
|
||||
long n = c - '0';
|
||||
while (--len > 0) {
|
||||
if (isDigit(c = b[off++]) &&
|
||||
(n < OVERFLOW_LIMIT || (n == OVERFLOW_LIMIT && (c - '0') < 8))) {
|
||||
n = n * 10 + c - '0';
|
||||
} else {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
95
java/org/apache/tomcat/util/buf/Asn1Parser.java
Normal file
95
java/org/apache/tomcat/util/buf/Asn1Parser.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* This is a very basic ASN.1 parser that provides the limited functionality
|
||||
* required by Tomcat. It is a long way from a complete parser.
|
||||
*
|
||||
* TODO: Consider extending this parser and refactoring the SpnegoTokenFixer to
|
||||
* use it.
|
||||
*/
|
||||
public class Asn1Parser {
|
||||
|
||||
private static final StringManager sm = StringManager.getManager(Asn1Parser.class);
|
||||
|
||||
private final byte[] source;
|
||||
|
||||
private int pos = 0;
|
||||
|
||||
|
||||
public Asn1Parser(byte[] source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
|
||||
public void parseTag(int tag) {
|
||||
int value = next();
|
||||
if (value != tag) {
|
||||
throw new IllegalArgumentException(sm.getString("asn1Parser.tagMismatch",
|
||||
Integer.valueOf(tag), Integer.valueOf(value)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void parseFullLength() {
|
||||
int len = parseLength();
|
||||
if (len + pos != source.length) {
|
||||
throw new IllegalArgumentException(sm.getString("asn1Parser.lengthInvalid",
|
||||
Integer.valueOf(len), Integer.valueOf(source.length - pos)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int parseLength() {
|
||||
int len = next();
|
||||
if (len > 127) {
|
||||
int bytes = len - 128;
|
||||
len = 0;
|
||||
for (int i = 0; i < bytes; i++) {
|
||||
len = len << 8;
|
||||
len = len + next();
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
public BigInteger parseInt() {
|
||||
parseTag(0x02);
|
||||
int len = parseLength();
|
||||
byte[] val = new byte[len];
|
||||
System.arraycopy(source, pos, val, 0, len);
|
||||
pos += len;
|
||||
return new BigInteger(val);
|
||||
}
|
||||
|
||||
|
||||
public void parseBytes(byte[] dest) {
|
||||
System.arraycopy(source, pos, dest, 0, dest.length);
|
||||
pos += dest.length;
|
||||
}
|
||||
|
||||
|
||||
private int next() {
|
||||
return source[pos++] & 0xFF;
|
||||
}
|
||||
}
|
||||
95
java/org/apache/tomcat/util/buf/Asn1Writer.java
Normal file
95
java/org/apache/tomcat/util/buf/Asn1Writer.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
public class Asn1Writer {
|
||||
|
||||
public static byte[] writeSequence(byte[]... components) {
|
||||
int len = 0;
|
||||
for (byte[] component : components) {
|
||||
len += component.length;
|
||||
}
|
||||
|
||||
byte[] combined = new byte[len];
|
||||
int pos = 0;
|
||||
for (byte[] component : components) {
|
||||
System.arraycopy(component, 0, combined, pos, component.length);
|
||||
pos += component.length;
|
||||
}
|
||||
|
||||
return writeTag((byte) 0x30, combined);
|
||||
}
|
||||
|
||||
|
||||
public static byte[] writeInteger(int value) {
|
||||
// How many bytes required to write the value? No more than 4 for int.
|
||||
int valueSize = 1;
|
||||
while ((value >> (valueSize * 8)) > 0) {
|
||||
valueSize++;
|
||||
}
|
||||
|
||||
byte[] valueBytes = new byte[valueSize];
|
||||
int i = 0;
|
||||
while (valueSize > 0) {
|
||||
valueBytes[i] = (byte) (value >> (8 * (valueSize - 1)));
|
||||
value = value >> 8;
|
||||
valueSize--;
|
||||
i++;
|
||||
}
|
||||
|
||||
return writeTag((byte) 0x02, valueBytes);
|
||||
}
|
||||
|
||||
public static byte[] writeOctetString(byte[] data) {
|
||||
return writeTag((byte) 0x04, data);
|
||||
}
|
||||
|
||||
public static byte[] writeTag(byte tagId, byte[] data) {
|
||||
int dataSize = data.length;
|
||||
// How many bytes to write the length?
|
||||
int lengthSize = 1;
|
||||
if (dataSize >127) {
|
||||
// 1 byte we have is now used to record how many bytes we need to
|
||||
// record a length > 127
|
||||
// Result is lengthSize = 1 + number of bytes to record length
|
||||
do {
|
||||
lengthSize++;
|
||||
}
|
||||
while ((dataSize >> (lengthSize * 8)) > 0);
|
||||
}
|
||||
|
||||
// 1 for tag + lengthSize + dataSize
|
||||
byte[] result = new byte[1 + lengthSize + dataSize];
|
||||
result[0] = tagId;
|
||||
if (dataSize < 128) {
|
||||
result[1] = (byte) dataSize;
|
||||
} else {
|
||||
// lengthSize is 1 + number of bytes for length
|
||||
result[1] = (byte) (127 + lengthSize);
|
||||
int i = lengthSize;
|
||||
while (dataSize > 0) {
|
||||
result[i] = (byte) (dataSize & 0xFF);
|
||||
dataSize = dataSize >> 8;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
System.arraycopy(data, 0, result, 1 + lengthSize, data.length);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
284
java/org/apache/tomcat/util/buf/B2CConverter.java
Normal file
284
java/org/apache/tomcat/util/buf/B2CConverter.java
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* NIO based character decoder.
|
||||
*/
|
||||
public class B2CConverter {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(Constants.Package);
|
||||
|
||||
private static final CharsetCache charsetCache = new CharsetCache();
|
||||
|
||||
|
||||
// Protected so unit tests can use it
|
||||
protected static final int LEFTOVER_SIZE = 9;
|
||||
|
||||
/**
|
||||
* Obtain the Charset for the given encoding
|
||||
*
|
||||
* @param enc The name of the encoding for the required charset
|
||||
*
|
||||
* @return The Charset corresponding to the requested encoding
|
||||
*
|
||||
* @throws UnsupportedEncodingException If the requested Charset is not
|
||||
* available
|
||||
*/
|
||||
public static Charset getCharset(String enc)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
// Encoding names should all be ASCII
|
||||
String lowerCaseEnc = enc.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
return getCharsetLower(lowerCaseEnc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Only to be used when it is known that the encoding name is in lower case.
|
||||
* @param lowerCaseEnc The name of the encoding for the required charset in
|
||||
* lower case
|
||||
*
|
||||
* @return The Charset corresponding to the requested encoding
|
||||
*
|
||||
* @throws UnsupportedEncodingException If the requested Charset is not
|
||||
* available
|
||||
*
|
||||
* @deprecated Will be removed in Tomcat 9.0.x
|
||||
*/
|
||||
@Deprecated
|
||||
public static Charset getCharsetLower(String lowerCaseEnc)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
Charset charset = charsetCache.getCharset(lowerCaseEnc);
|
||||
|
||||
if (charset == null) {
|
||||
// Pre-population of the cache means this must be invalid
|
||||
throw new UnsupportedEncodingException(
|
||||
sm.getString("b2cConverter.unknownEncoding", lowerCaseEnc));
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
|
||||
private final CharsetDecoder decoder;
|
||||
private ByteBuffer bb = null;
|
||||
private CharBuffer cb = null;
|
||||
|
||||
/**
|
||||
* Leftover buffer used for incomplete characters.
|
||||
*/
|
||||
private final ByteBuffer leftovers;
|
||||
|
||||
public B2CConverter(Charset charset) {
|
||||
this(charset, false);
|
||||
}
|
||||
|
||||
public B2CConverter(Charset charset, boolean replaceOnError) {
|
||||
byte[] left = new byte[LEFTOVER_SIZE];
|
||||
leftovers = ByteBuffer.wrap(left);
|
||||
CodingErrorAction action;
|
||||
if (replaceOnError) {
|
||||
action = CodingErrorAction.REPLACE;
|
||||
} else {
|
||||
action = CodingErrorAction.REPORT;
|
||||
}
|
||||
// Special case. Use the Apache Harmony based UTF-8 decoder because it
|
||||
// - a) rejects invalid sequences that the JVM decoder does not
|
||||
// - b) fails faster for some invalid sequences
|
||||
if (charset.equals(StandardCharsets.UTF_8)) {
|
||||
decoder = new Utf8Decoder();
|
||||
} else {
|
||||
decoder = charset.newDecoder();
|
||||
}
|
||||
decoder.onMalformedInput(action);
|
||||
decoder.onUnmappableCharacter(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the decoder state.
|
||||
*/
|
||||
public void recycle() {
|
||||
decoder.reset();
|
||||
leftovers.position(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given bytes to characters.
|
||||
*
|
||||
* @param bc byte input
|
||||
* @param cc char output
|
||||
* @param endOfInput Is this all of the available data
|
||||
*
|
||||
* @throws IOException If the conversion can not be completed
|
||||
*/
|
||||
public void convert(ByteChunk bc, CharChunk cc, boolean endOfInput)
|
||||
throws IOException {
|
||||
if ((bb == null) || (bb.array() != bc.getBuffer())) {
|
||||
// Create a new byte buffer if anything changed
|
||||
bb = ByteBuffer.wrap(bc.getBuffer(), bc.getStart(), bc.getLength());
|
||||
} else {
|
||||
// Initialize the byte buffer
|
||||
bb.limit(bc.getEnd());
|
||||
bb.position(bc.getStart());
|
||||
}
|
||||
if ((cb == null) || (cb.array() != cc.getBuffer())) {
|
||||
// Create a new char buffer if anything changed
|
||||
cb = CharBuffer.wrap(cc.getBuffer(), cc.getEnd(),
|
||||
cc.getBuffer().length - cc.getEnd());
|
||||
} else {
|
||||
// Initialize the char buffer
|
||||
cb.limit(cc.getBuffer().length);
|
||||
cb.position(cc.getEnd());
|
||||
}
|
||||
CoderResult result = null;
|
||||
// Parse leftover if any are present
|
||||
if (leftovers.position() > 0) {
|
||||
int pos = cb.position();
|
||||
// Loop until one char is decoded or there is a decoder error
|
||||
do {
|
||||
leftovers.put(bc.substractB());
|
||||
leftovers.flip();
|
||||
result = decoder.decode(leftovers, cb, endOfInput);
|
||||
leftovers.position(leftovers.limit());
|
||||
leftovers.limit(leftovers.array().length);
|
||||
} while (result.isUnderflow() && (cb.position() == pos));
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
}
|
||||
bb.position(bc.getStart());
|
||||
leftovers.position(0);
|
||||
}
|
||||
// Do the decoding and get the results into the byte chunk and the char
|
||||
// chunk
|
||||
result = decoder.decode(bb, cb, endOfInput);
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
} else if (result.isOverflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk, if
|
||||
// this continues the char buffer will get resized
|
||||
bc.setOffset(bb.position());
|
||||
cc.setEnd(cb.position());
|
||||
} else if (result.isUnderflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.setOffset(bb.position());
|
||||
cc.setEnd(cb.position());
|
||||
// Put leftovers in the leftovers byte buffer
|
||||
if (bc.getLength() > 0) {
|
||||
leftovers.limit(leftovers.array().length);
|
||||
leftovers.position(bc.getLength());
|
||||
bc.substract(leftovers.array(), 0, bc.getLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given bytes to characters.
|
||||
*
|
||||
* @param bc byte input
|
||||
* @param cc char output
|
||||
* @param ic byte input channel
|
||||
* @param endOfInput Is this all of the available data
|
||||
*
|
||||
* @throws IOException If the conversion can not be completed
|
||||
*/
|
||||
public void convert(ByteBuffer bc, CharBuffer cc, ByteChunk.ByteInputChannel ic, boolean endOfInput)
|
||||
throws IOException {
|
||||
if ((bb == null) || (bb.array() != bc.array())) {
|
||||
// Create a new byte buffer if anything changed
|
||||
bb = ByteBuffer.wrap(bc.array(), bc.arrayOffset() + bc.position(), bc.remaining());
|
||||
} else {
|
||||
// Initialize the byte buffer
|
||||
bb.limit(bc.limit());
|
||||
bb.position(bc.position());
|
||||
}
|
||||
if ((cb == null) || (cb.array() != cc.array())) {
|
||||
// Create a new char buffer if anything changed
|
||||
cb = CharBuffer.wrap(cc.array(), cc.limit(), cc.capacity() - cc.limit());
|
||||
} else {
|
||||
// Initialize the char buffer
|
||||
cb.limit(cc.capacity());
|
||||
cb.position(cc.limit());
|
||||
}
|
||||
CoderResult result = null;
|
||||
// Parse leftover if any are present
|
||||
if (leftovers.position() > 0) {
|
||||
int pos = cb.position();
|
||||
// Loop until one char is decoded or there is a decoder error
|
||||
do {
|
||||
byte chr;
|
||||
if (bc.remaining() == 0) {
|
||||
int n = ic.realReadBytes();
|
||||
chr = n < 0 ? -1 : bc.get();
|
||||
} else {
|
||||
chr = bc.get();
|
||||
}
|
||||
leftovers.put(chr);
|
||||
leftovers.flip();
|
||||
result = decoder.decode(leftovers, cb, endOfInput);
|
||||
leftovers.position(leftovers.limit());
|
||||
leftovers.limit(leftovers.array().length);
|
||||
} while (result.isUnderflow() && (cb.position() == pos));
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
}
|
||||
bb.position(bc.position());
|
||||
leftovers.position(0);
|
||||
}
|
||||
// Do the decoding and get the results into the byte chunk and the char
|
||||
// chunk
|
||||
result = decoder.decode(bb, cb, endOfInput);
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
} else if (result.isOverflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk, if
|
||||
// this continues the char buffer will get resized
|
||||
bc.position(bb.position());
|
||||
cc.limit(cb.position());
|
||||
} else if (result.isUnderflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.position(bb.position());
|
||||
cc.limit(cb.position());
|
||||
// Put leftovers in the leftovers byte buffer
|
||||
if (bc.remaining() > 0) {
|
||||
leftovers.limit(leftovers.array().length);
|
||||
leftovers.position(bc.remaining());
|
||||
bc.get(leftovers.array(), 0, bc.remaining());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Charset getCharset() {
|
||||
return decoder.charset();
|
||||
}
|
||||
}
|
||||
55
java/org/apache/tomcat/util/buf/ByteBufferHolder.java
Normal file
55
java/org/apache/tomcat/util/buf/ByteBufferHolder.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Simple wrapper for a {@link ByteBuffer} that remembers if the buffer has been
|
||||
* flipped or not.
|
||||
*/
|
||||
public class ByteBufferHolder {
|
||||
|
||||
private final ByteBuffer buf;
|
||||
private final AtomicBoolean flipped;
|
||||
|
||||
public ByteBufferHolder(ByteBuffer buf, boolean flipped) {
|
||||
this.buf = buf;
|
||||
this.flipped = new AtomicBoolean(flipped);
|
||||
}
|
||||
|
||||
|
||||
public ByteBuffer getBuf() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFlipped() {
|
||||
return flipped.get();
|
||||
}
|
||||
|
||||
|
||||
public boolean flip() {
|
||||
if (flipped.compareAndSet(false, true)) {
|
||||
buf.flip();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
144
java/org/apache/tomcat/util/buf/ByteBufferUtils.java
Normal file
144
java/org/apache/tomcat/util/buf/ByteBufferUtils.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.compat.JreCompat;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
public class ByteBufferUtils {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(Constants.Package);
|
||||
private static final Log log = LogFactory.getLog(ByteBufferUtils.class);
|
||||
|
||||
private static final Object unsafe;
|
||||
private static final Method cleanerMethod;
|
||||
private static final Method cleanMethod;
|
||||
private static final Method invokeCleanerMethod;
|
||||
|
||||
static {
|
||||
ByteBuffer tempBuffer = ByteBuffer.allocateDirect(0);
|
||||
Method cleanerMethodLocal = null;
|
||||
Method cleanMethodLocal = null;
|
||||
Object unsafeLocal = null;
|
||||
Method invokeCleanerMethodLocal = null;
|
||||
if (JreCompat.isJre9Available()) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName("sun.misc.Unsafe");
|
||||
Field theUnsafe = clazz.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
unsafeLocal = theUnsafe.get(null);
|
||||
invokeCleanerMethodLocal = clazz.getMethod("invokeCleaner", ByteBuffer.class);
|
||||
invokeCleanerMethodLocal.invoke(unsafeLocal, tempBuffer);
|
||||
} catch (IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException | SecurityException
|
||||
| ClassNotFoundException | NoSuchFieldException e) {
|
||||
log.warn(sm.getString("byteBufferUtils.cleaner"), e);
|
||||
unsafeLocal = null;
|
||||
invokeCleanerMethodLocal = null;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
cleanerMethodLocal = tempBuffer.getClass().getMethod("cleaner");
|
||||
cleanerMethodLocal.setAccessible(true);
|
||||
Object cleanerObject = cleanerMethodLocal.invoke(tempBuffer);
|
||||
cleanMethodLocal = cleanerObject.getClass().getMethod("clean");
|
||||
cleanMethodLocal.invoke(cleanerObject);
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException |
|
||||
IllegalArgumentException | InvocationTargetException e) {
|
||||
log.warn(sm.getString("byteBufferUtils.cleaner"), e);
|
||||
cleanerMethodLocal = null;
|
||||
cleanMethodLocal = null;
|
||||
}
|
||||
}
|
||||
cleanerMethod = cleanerMethodLocal;
|
||||
cleanMethod = cleanMethodLocal;
|
||||
unsafe = unsafeLocal;
|
||||
invokeCleanerMethod = invokeCleanerMethodLocal;
|
||||
}
|
||||
|
||||
private ByteBufferUtils() {
|
||||
// Hide the default constructor since this is a utility class.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expands buffer to the given size unless it is already as big or bigger.
|
||||
* Buffers are assumed to be in 'write to' mode since there would be no need
|
||||
* to expand a buffer while it was in 'read from' mode.
|
||||
*
|
||||
* @param in Buffer to expand
|
||||
* @param newSize The size t which the buffer should be expanded
|
||||
* @return The expanded buffer with any data from the input buffer
|
||||
* copied in to it or the original buffer if there was no
|
||||
* need for expansion
|
||||
*/
|
||||
public static ByteBuffer expand(ByteBuffer in, int newSize) {
|
||||
if (in.capacity() >= newSize) {
|
||||
return in;
|
||||
}
|
||||
|
||||
ByteBuffer out;
|
||||
boolean direct = false;
|
||||
if (in.isDirect()) {
|
||||
out = ByteBuffer.allocateDirect(newSize);
|
||||
direct = true;
|
||||
} else {
|
||||
out = ByteBuffer.allocate(newSize);
|
||||
}
|
||||
|
||||
// Copy data
|
||||
in.flip();
|
||||
out.put(in);
|
||||
|
||||
if (direct) {
|
||||
cleanDirectBuffer(in);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static void cleanDirectBuffer(ByteBuffer buf) {
|
||||
if (cleanMethod != null) {
|
||||
try {
|
||||
cleanMethod.invoke(cleanerMethod.invoke(buf));
|
||||
} catch (IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | SecurityException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("byteBufferUtils.cleaner"), e);
|
||||
}
|
||||
}
|
||||
} else if (invokeCleanerMethod != null) {
|
||||
try {
|
||||
invokeCleanerMethod.invoke(unsafe, buf);
|
||||
} catch (IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | SecurityException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("byteBufferUtils.cleaner"), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
834
java/org/apache/tomcat/util/buf/ByteChunk.java
Normal file
834
java/org/apache/tomcat/util/buf/ByteChunk.java
Normal file
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/*
|
||||
* In a server it is very important to be able to operate on
|
||||
* the original byte[] without converting everything to chars.
|
||||
* Some protocols are ASCII only, and some allow different
|
||||
* non-UNICODE encodings. The encoding is not known beforehand,
|
||||
* and can even change during the execution of the protocol.
|
||||
* ( for example a multipart message may have parts with different
|
||||
* encoding )
|
||||
*
|
||||
* For HTTP it is not very clear how the encoding of RequestURI
|
||||
* and mime values can be determined, but it is a great advantage
|
||||
* to be able to parse the request without converting to string.
|
||||
*/
|
||||
|
||||
// TODO: This class could either extend ByteBuffer, or better a ByteBuffer
|
||||
// inside this way it could provide the search/etc on ByteBuffer, as a helper.
|
||||
|
||||
/**
|
||||
* This class is used to represent a chunk of bytes, and utilities to manipulate
|
||||
* byte[].
|
||||
*
|
||||
* The buffer can be modified and used for both input and output.
|
||||
*
|
||||
* There are 2 modes: The chunk can be associated with a sink - ByteInputChannel
|
||||
* or ByteOutputChannel, which will be used when the buffer is empty (on input)
|
||||
* or filled (on output). For output, it can also grow. This operating mode is
|
||||
* selected by calling setLimit() or allocate(initial, limit) with limit != -1.
|
||||
*
|
||||
* Various search and append method are defined - similar with String and
|
||||
* StringBuffer, but operating on bytes.
|
||||
*
|
||||
* This is important because it allows processing the http headers directly on
|
||||
* the received bytes, without converting to chars and Strings until the strings
|
||||
* are needed. In addition, the charset is determined later, from headers or
|
||||
* user code.
|
||||
*
|
||||
* @author dac@sun.com
|
||||
* @author James Todd [gonzo@sun.com]
|
||||
* @author Costin Manolache
|
||||
* @author Remy Maucherat
|
||||
*/
|
||||
public final class ByteChunk extends AbstractChunk {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Input interface, used when the buffer is empty.
|
||||
*
|
||||
* Same as java.nio.channels.ReadableByteChannel
|
||||
*/
|
||||
public static interface ByteInputChannel {
|
||||
|
||||
/**
|
||||
* Read new bytes.
|
||||
*
|
||||
* @return The number of bytes read
|
||||
*
|
||||
* @throws IOException If an I/O error occurs during reading
|
||||
*/
|
||||
public int realReadBytes() throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* When we need more space we'll either grow the buffer ( up to the limit )
|
||||
* or send it to a channel.
|
||||
*
|
||||
* Same as java.nio.channel.WritableByteChannel.
|
||||
*/
|
||||
public static interface ByteOutputChannel {
|
||||
|
||||
/**
|
||||
* Send the bytes ( usually the internal conversion buffer ). Expect 8k
|
||||
* output if the buffer is full.
|
||||
*
|
||||
* @param buf bytes that will be written
|
||||
* @param off offset in the bytes array
|
||||
* @param len length that will be written
|
||||
* @throws IOException If an I/O occurs while writing the bytes
|
||||
*/
|
||||
public void realWriteBytes(byte buf[], int off, int len) throws IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Send the bytes ( usually the internal conversion buffer ). Expect 8k
|
||||
* output if the buffer is full.
|
||||
*
|
||||
* @param from bytes that will be written
|
||||
* @throws IOException If an I/O occurs while writing the bytes
|
||||
*/
|
||||
public void realWriteBytes(ByteBuffer from) throws IOException;
|
||||
}
|
||||
|
||||
// --------------------
|
||||
|
||||
/**
|
||||
* Default encoding used to convert to strings. It should be UTF8, as most
|
||||
* standards seem to converge, but the servlet API requires 8859_1, and this
|
||||
* object is used mostly for servlets.
|
||||
*/
|
||||
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
|
||||
|
||||
private transient Charset charset;
|
||||
|
||||
// byte[]
|
||||
private byte[] buff;
|
||||
|
||||
// transient as serialization is primarily for values via, e.g. JMX
|
||||
private transient ByteInputChannel in = null;
|
||||
private transient ByteOutputChannel out = null;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new, uninitialized ByteChunk object.
|
||||
*/
|
||||
public ByteChunk() {
|
||||
}
|
||||
|
||||
|
||||
public ByteChunk(int initial) {
|
||||
allocate(initial, -1);
|
||||
}
|
||||
|
||||
|
||||
private void writeObject(ObjectOutputStream oos) throws IOException {
|
||||
oos.defaultWriteObject();
|
||||
oos.writeUTF(getCharset().name());
|
||||
}
|
||||
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||
ois.defaultReadObject();
|
||||
this.charset = Charset.forName(ois.readUTF());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
super.recycle();
|
||||
charset = null;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Setup --------------------
|
||||
|
||||
public void allocate(int initial, int limit) {
|
||||
if (buff == null || buff.length < initial) {
|
||||
buff = new byte[initial];
|
||||
}
|
||||
setLimit(limit);
|
||||
start = 0;
|
||||
end = 0;
|
||||
isSet = true;
|
||||
hasHashCode = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the buffer to the specified subarray of bytes.
|
||||
*
|
||||
* @param b the ascii bytes
|
||||
* @param off the start offset of the bytes
|
||||
* @param len the length of the bytes
|
||||
*/
|
||||
public void setBytes(byte[] b, int off, int len) {
|
||||
buff = b;
|
||||
start = off;
|
||||
end = start + len;
|
||||
isSet = true;
|
||||
hasHashCode = false;
|
||||
}
|
||||
|
||||
|
||||
public void setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
|
||||
public Charset getCharset() {
|
||||
if (charset == null) {
|
||||
charset = DEFAULT_CHARSET;
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the buffer.
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return getBuffer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the buffer.
|
||||
*/
|
||||
public byte[] getBuffer() {
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the buffer is empty, read the data from the input channel.
|
||||
*
|
||||
* @param in The input channel
|
||||
*/
|
||||
public void setByteInputChannel(ByteInputChannel in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the buffer is full, write the data to the output channel. Also used
|
||||
* when large amount of data is appended. If not set, the buffer will grow
|
||||
* to the limit.
|
||||
*
|
||||
* @param out The output channel
|
||||
*/
|
||||
public void setByteOutputChannel(ByteOutputChannel out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Adding data to the buffer --------------------
|
||||
|
||||
public void append(byte b) throws IOException {
|
||||
makeSpace(1);
|
||||
int limit = getLimitInternal();
|
||||
|
||||
// couldn't make space
|
||||
if (end >= limit) {
|
||||
flushBuffer();
|
||||
}
|
||||
buff[end++] = b;
|
||||
}
|
||||
|
||||
|
||||
public void append(ByteChunk src) throws IOException {
|
||||
append(src.getBytes(), src.getStart(), src.getLength());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add data to the buffer.
|
||||
*
|
||||
* @param src Bytes array
|
||||
* @param off Offset
|
||||
* @param len Length
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void append(byte src[], int off, int len) throws IOException {
|
||||
// will grow, up to limit
|
||||
makeSpace(len);
|
||||
int limit = getLimitInternal();
|
||||
|
||||
// Optimize on a common case.
|
||||
// If the buffer is empty and the source is going to fill up all the
|
||||
// space in buffer, may as well write it directly to the output,
|
||||
// and avoid an extra copy
|
||||
if (len == limit && end == start && out != null) {
|
||||
out.realWriteBytes(src, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we are below the limit
|
||||
if (len <= limit - end) {
|
||||
System.arraycopy(src, off, buff, end, len);
|
||||
end += len;
|
||||
return;
|
||||
}
|
||||
|
||||
// Need more space than we can afford, need to flush buffer.
|
||||
|
||||
// The buffer is already at (or bigger than) limit.
|
||||
|
||||
// We chunk the data into slices fitting in the buffer limit, although
|
||||
// if the data is written directly if it doesn't fit.
|
||||
|
||||
int avail = limit - end;
|
||||
System.arraycopy(src, off, buff, end, avail);
|
||||
end += avail;
|
||||
|
||||
flushBuffer();
|
||||
|
||||
int remain = len - avail;
|
||||
|
||||
while (remain > (limit - end)) {
|
||||
out.realWriteBytes(src, (off + len) - remain, limit - end);
|
||||
remain = remain - (limit - end);
|
||||
}
|
||||
|
||||
System.arraycopy(src, (off + len) - remain, buff, end, remain);
|
||||
end += remain;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add data to the buffer.
|
||||
*
|
||||
* @param from the ByteBuffer with the data
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void append(ByteBuffer from) throws IOException {
|
||||
int len = from.remaining();
|
||||
|
||||
// will grow, up to limit
|
||||
makeSpace(len);
|
||||
int limit = getLimitInternal();
|
||||
|
||||
// Optimize on a common case.
|
||||
// If the buffer is empty and the source is going to fill up all the
|
||||
// space in buffer, may as well write it directly to the output,
|
||||
// and avoid an extra copy
|
||||
if (len == limit && end == start && out != null) {
|
||||
out.realWriteBytes(from);
|
||||
from.position(from.limit());
|
||||
return;
|
||||
}
|
||||
// if we have limit and we're below
|
||||
if (len <= limit - end) {
|
||||
// makeSpace will grow the buffer to the limit,
|
||||
// so we have space
|
||||
from.get(buff, end, len);
|
||||
end += len;
|
||||
return;
|
||||
}
|
||||
|
||||
// need more space than we can afford, need to flush
|
||||
// buffer
|
||||
|
||||
// the buffer is already at ( or bigger than ) limit
|
||||
|
||||
// We chunk the data into slices fitting in the buffer limit, although
|
||||
// if the data is written directly if it doesn't fit
|
||||
|
||||
int avail = limit - end;
|
||||
from.get(buff, end, avail);
|
||||
end += avail;
|
||||
|
||||
flushBuffer();
|
||||
|
||||
int fromLimit = from.limit();
|
||||
int remain = len - avail;
|
||||
avail = limit - end;
|
||||
while (remain >= avail) {
|
||||
from.limit(from.position() + avail);
|
||||
out.realWriteBytes(from);
|
||||
from.position(from.limit());
|
||||
remain = remain - avail;
|
||||
}
|
||||
|
||||
from.limit(fromLimit);
|
||||
from.get(buff, end, remain);
|
||||
end += remain;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Removing data from the buffer --------------------
|
||||
|
||||
public int substract() throws IOException {
|
||||
if (checkEof()) {
|
||||
return -1;
|
||||
}
|
||||
return buff[start++] & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
public byte substractB() throws IOException {
|
||||
if (checkEof()) {
|
||||
return -1;
|
||||
}
|
||||
return buff[start++];
|
||||
}
|
||||
|
||||
|
||||
public int substract(byte dest[], int off, int len) throws IOException {
|
||||
if (checkEof()) {
|
||||
return -1;
|
||||
}
|
||||
int n = len;
|
||||
if (len > getLength()) {
|
||||
n = getLength();
|
||||
}
|
||||
System.arraycopy(buff, start, dest, off, n);
|
||||
start += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transfers bytes from the buffer to the specified ByteBuffer. After the
|
||||
* operation the position of the ByteBuffer will be returned to the one
|
||||
* before the operation, the limit will be the position incremented by the
|
||||
* number of the transfered bytes.
|
||||
*
|
||||
* @param to the ByteBuffer into which bytes are to be written.
|
||||
* @return an integer specifying the actual number of bytes read, or -1 if
|
||||
* the end of the stream is reached
|
||||
* @throws IOException if an input or output exception has occurred
|
||||
*/
|
||||
public int substract(ByteBuffer to) throws IOException {
|
||||
if (checkEof()) {
|
||||
return -1;
|
||||
}
|
||||
int n = Math.min(to.remaining(), getLength());
|
||||
to.put(buff, start, n);
|
||||
to.limit(to.position());
|
||||
to.position(to.position() - n);
|
||||
start += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkEof() throws IOException {
|
||||
if ((end - start) == 0) {
|
||||
if (in == null) {
|
||||
return true;
|
||||
}
|
||||
int n = in.realReadBytes();
|
||||
if (n < 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the buffer to the sink. Called by append() when the limit is
|
||||
* reached. You can also call it explicitly to force the data to be written.
|
||||
*
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void flushBuffer() throws IOException {
|
||||
// assert out!=null
|
||||
if (out == null) {
|
||||
throw new IOException("Buffer overflow, no sink " + getLimit() + " " + buff.length);
|
||||
}
|
||||
out.realWriteBytes(buff, start, end - start);
|
||||
end = start;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make space for len bytes. If len is small, allocate a reserve space too.
|
||||
* Never grow bigger than the limit or {@link AbstractChunk#ARRAY_MAX_SIZE}.
|
||||
*
|
||||
* @param count The size
|
||||
*/
|
||||
public void makeSpace(int count) {
|
||||
byte[] tmp = null;
|
||||
|
||||
int limit = getLimitInternal();
|
||||
|
||||
long newSize;
|
||||
long desiredSize = end + count;
|
||||
|
||||
// Can't grow above the limit
|
||||
if (desiredSize > limit) {
|
||||
desiredSize = limit;
|
||||
}
|
||||
|
||||
if (buff == null) {
|
||||
if (desiredSize < 256) {
|
||||
desiredSize = 256; // take a minimum
|
||||
}
|
||||
buff = new byte[(int) desiredSize];
|
||||
}
|
||||
|
||||
// limit < buf.length (the buffer is already big)
|
||||
// or we already have space XXX
|
||||
if (desiredSize <= buff.length) {
|
||||
return;
|
||||
}
|
||||
// grow in larger chunks
|
||||
if (desiredSize < 2L * buff.length) {
|
||||
newSize = buff.length * 2L;
|
||||
} else {
|
||||
newSize = buff.length * 2L + count;
|
||||
}
|
||||
|
||||
if (newSize > limit) {
|
||||
newSize = limit;
|
||||
}
|
||||
tmp = new byte[(int) newSize];
|
||||
|
||||
// Compacts buffer
|
||||
System.arraycopy(buff, start, tmp, 0, end - start);
|
||||
buff = tmp;
|
||||
tmp = null;
|
||||
end = end - start;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Conversion and getters --------------------
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (null == buff) {
|
||||
return null;
|
||||
} else if (end - start == 0) {
|
||||
return "";
|
||||
}
|
||||
return StringCache.toString(this);
|
||||
}
|
||||
|
||||
|
||||
public String toStringInternal() {
|
||||
if (charset == null) {
|
||||
charset = DEFAULT_CHARSET;
|
||||
}
|
||||
// new String(byte[], int, int, Charset) takes a defensive copy of the
|
||||
// entire byte array. This is expensive if only a small subset of the
|
||||
// bytes will be used. The code below is from Apache Harmony.
|
||||
CharBuffer cb = charset.decode(ByteBuffer.wrap(buff, start, end - start));
|
||||
return new String(cb.array(), cb.arrayOffset(), cb.length());
|
||||
}
|
||||
|
||||
|
||||
public long getLong() {
|
||||
return Ascii.parseLong(buff, start, end - start);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- equals --------------------
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ByteChunk) {
|
||||
return equals((ByteChunk) obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the message bytes to the specified String object.
|
||||
*
|
||||
* @param s the String to compare
|
||||
* @return <code>true</code> if the comparison succeeded, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equals(String s) {
|
||||
// XXX ENCODING - this only works if encoding is UTF8-compat
|
||||
// ( ok for tomcat, where we compare ascii - header names, etc )!!!
|
||||
|
||||
byte[] b = buff;
|
||||
int len = end - start;
|
||||
if (b == null || len != s.length()) {
|
||||
return false;
|
||||
}
|
||||
int off = start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (b[off++] != s.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the message bytes to the specified String object.
|
||||
*
|
||||
* @param s the String to compare
|
||||
* @return <code>true</code> if the comparison succeeded, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equalsIgnoreCase(String s) {
|
||||
byte[] b = buff;
|
||||
int len = end - start;
|
||||
if (b == null || len != s.length()) {
|
||||
return false;
|
||||
}
|
||||
int off = start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (Ascii.toLower(b[off++]) != Ascii.toLower(s.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(ByteChunk bb) {
|
||||
return equals(bb.getBytes(), bb.getStart(), bb.getLength());
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(byte b2[], int off2, int len2) {
|
||||
byte b1[] = buff;
|
||||
if (b1 == null && b2 == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int len = end - start;
|
||||
if (len != len2 || b1 == null || b2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int off1 = start;
|
||||
|
||||
while (len-- > 0) {
|
||||
if (b1[off1++] != b2[off2++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(CharChunk cc) {
|
||||
return equals(cc.getChars(), cc.getStart(), cc.getLength());
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(char c2[], int off2, int len2) {
|
||||
// XXX works only for enc compatible with ASCII/UTF !!!
|
||||
byte b1[] = buff;
|
||||
if (c2 == null && b1 == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (b1 == null || c2 == null || end - start != len2) {
|
||||
return false;
|
||||
}
|
||||
int off1 = start;
|
||||
int len = end - start;
|
||||
|
||||
while (len-- > 0) {
|
||||
if ((char) b1[off1++] != c2[off2++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the buffer starts with the specified string when tested
|
||||
* in a case sensitive manner.
|
||||
*
|
||||
* @param s the string
|
||||
* @param pos The position
|
||||
*
|
||||
* @return <code>true</code> if the start matches
|
||||
*/
|
||||
public boolean startsWith(String s, int pos) {
|
||||
byte[] b = buff;
|
||||
int len = s.length();
|
||||
if (b == null || len + pos > end - start) {
|
||||
return false;
|
||||
}
|
||||
int off = start + pos;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (b[off++] != s.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the buffer starts with the specified string when tested
|
||||
* in a case insensitive manner.
|
||||
*
|
||||
* @param s the string
|
||||
* @param pos The position
|
||||
*
|
||||
* @return <code>true</code> if the start matches
|
||||
*/
|
||||
public boolean startsWithIgnoreCase(String s, int pos) {
|
||||
byte[] b = buff;
|
||||
int len = s.length();
|
||||
if (b == null || len + pos > end - start) {
|
||||
return false;
|
||||
}
|
||||
int off = start + pos;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (Ascii.toLower(b[off++]) != Ascii.toLower(s.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int getBufferElement(int index) {
|
||||
return buff[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first instance of the given character in this ByteChunk
|
||||
* starting at the specified byte. If the character is not found, -1 is
|
||||
* returned. <br>
|
||||
* NOTE: This only works for characters in the range 0-127.
|
||||
*
|
||||
* @param c The character
|
||||
* @param starting The start position
|
||||
* @return The position of the first instance of the character or -1 if the
|
||||
* character is not found.
|
||||
*/
|
||||
public int indexOf(char c, int starting) {
|
||||
int ret = indexOf(buff, start + starting, end, c);
|
||||
return (ret >= start) ? ret - start : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first instance of the given character in the given byte array
|
||||
* between the specified start and end. <br>
|
||||
* NOTE: This only works for characters in the range 0-127.
|
||||
*
|
||||
* @param bytes The array to search
|
||||
* @param start The point to start searching from in the array
|
||||
* @param end The point to stop searching in the array
|
||||
* @param s The character to search for
|
||||
* @return The position of the first instance of the character or -1 if the
|
||||
* character is not found.
|
||||
*/
|
||||
public static int indexOf(byte bytes[], int start, int end, char s) {
|
||||
int offset = start;
|
||||
|
||||
while (offset < end) {
|
||||
byte b = bytes[offset];
|
||||
if (b == s) {
|
||||
return offset;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first instance of the given byte in the byte array between
|
||||
* the specified start and end.
|
||||
*
|
||||
* @param bytes The byte array to search
|
||||
* @param start The point to start searching from in the byte array
|
||||
* @param end The point to stop searching in the byte array
|
||||
* @param b The byte to search for
|
||||
* @return The position of the first instance of the byte or -1 if the byte
|
||||
* is not found.
|
||||
*/
|
||||
public static int findByte(byte bytes[], int start, int end, byte b) {
|
||||
int offset = start;
|
||||
while (offset < end) {
|
||||
if (bytes[offset] == b) {
|
||||
return offset;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first instance of any of the given bytes in the byte array
|
||||
* between the specified start and end.
|
||||
*
|
||||
* @param bytes The byte array to search
|
||||
* @param start The point to start searching from in the byte array
|
||||
* @param end The point to stop searching in the byte array
|
||||
* @param b The array of bytes to search for
|
||||
* @return The position of the first instance of the byte or -1 if the byte
|
||||
* is not found.
|
||||
*/
|
||||
public static int findBytes(byte bytes[], int start, int end, byte b[]) {
|
||||
int blen = b.length;
|
||||
int offset = start;
|
||||
while (offset < end) {
|
||||
for (int i = 0; i < blen; i++) {
|
||||
if (bytes[offset] == b[i]) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert specified String to a byte array. This ONLY WORKS for ascii, UTF
|
||||
* chars will be truncated.
|
||||
*
|
||||
* @param value to convert to byte array
|
||||
* @return the byte array value
|
||||
*/
|
||||
public static final byte[] convertToBytes(String value) {
|
||||
byte[] result = new byte[value.length()];
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
result[i] = (byte) value.charAt(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
192
java/org/apache/tomcat/util/buf/C2BConverter.java
Normal file
192
java/org/apache/tomcat/util/buf/C2BConverter.java
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
|
||||
/**
|
||||
* NIO based character encoder.
|
||||
*/
|
||||
public final class C2BConverter {
|
||||
|
||||
private final CharsetEncoder encoder;
|
||||
private ByteBuffer bb = null;
|
||||
private CharBuffer cb = null;
|
||||
|
||||
/**
|
||||
* Leftover buffer used for multi-characters characters.
|
||||
*/
|
||||
private final CharBuffer leftovers;
|
||||
|
||||
public C2BConverter(Charset charset) {
|
||||
encoder = charset.newEncoder();
|
||||
encoder.onUnmappableCharacter(CodingErrorAction.REPLACE)
|
||||
.onMalformedInput(CodingErrorAction.REPLACE);
|
||||
char[] left = new char[4];
|
||||
leftovers = CharBuffer.wrap(left);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the encoder state.
|
||||
*/
|
||||
public void recycle() {
|
||||
encoder.reset();
|
||||
leftovers.position(0);
|
||||
}
|
||||
|
||||
public boolean isUndeflow() {
|
||||
return (leftovers.position() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given characters to bytes.
|
||||
*
|
||||
* @param cc char input
|
||||
* @param bc byte output
|
||||
* @throws IOException An encoding error occurred
|
||||
*/
|
||||
public void convert(CharChunk cc, ByteChunk bc) throws IOException {
|
||||
if ((bb == null) || (bb.array() != bc.getBuffer())) {
|
||||
// Create a new byte buffer if anything changed
|
||||
bb = ByteBuffer.wrap(bc.getBuffer(), bc.getEnd(), bc.getBuffer().length - bc.getEnd());
|
||||
} else {
|
||||
// Initialize the byte buffer
|
||||
bb.limit(bc.getBuffer().length);
|
||||
bb.position(bc.getEnd());
|
||||
}
|
||||
if ((cb == null) || (cb.array() != cc.getBuffer())) {
|
||||
// Create a new char buffer if anything changed
|
||||
cb = CharBuffer.wrap(cc.getBuffer(), cc.getStart(), cc.getLength());
|
||||
} else {
|
||||
// Initialize the char buffer
|
||||
cb.limit(cc.getEnd());
|
||||
cb.position(cc.getStart());
|
||||
}
|
||||
CoderResult result = null;
|
||||
// Parse leftover if any are present
|
||||
if (leftovers.position() > 0) {
|
||||
int pos = bb.position();
|
||||
// Loop until one char is encoded or there is a encoder error
|
||||
do {
|
||||
leftovers.put((char) cc.substract());
|
||||
leftovers.flip();
|
||||
result = encoder.encode(leftovers, bb, false);
|
||||
leftovers.position(leftovers.limit());
|
||||
leftovers.limit(leftovers.array().length);
|
||||
} while (result.isUnderflow() && (bb.position() == pos));
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
}
|
||||
cb.position(cc.getStart());
|
||||
leftovers.position(0);
|
||||
}
|
||||
// Do the decoding and get the results into the byte chunk and the char
|
||||
// chunk
|
||||
result = encoder.encode(cb, bb, false);
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
} else if (result.isOverflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.setEnd(bb.position());
|
||||
cc.setOffset(cb.position());
|
||||
} else if (result.isUnderflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.setEnd(bb.position());
|
||||
cc.setOffset(cb.position());
|
||||
// Put leftovers in the leftovers char buffer
|
||||
if (cc.getLength() > 0) {
|
||||
leftovers.limit(leftovers.array().length);
|
||||
leftovers.position(cc.getLength());
|
||||
cc.substract(leftovers.array(), 0, cc.getLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given characters to bytes.
|
||||
*
|
||||
* @param cc char input
|
||||
* @param bc byte output
|
||||
* @throws IOException An encoding error occurred
|
||||
*/
|
||||
public void convert(CharBuffer cc, ByteBuffer bc) throws IOException {
|
||||
if ((bb == null) || (bb.array() != bc.array())) {
|
||||
// Create a new byte buffer if anything changed
|
||||
bb = ByteBuffer.wrap(bc.array(), bc.limit(), bc.capacity() - bc.limit());
|
||||
} else {
|
||||
// Initialize the byte buffer
|
||||
bb.limit(bc.capacity());
|
||||
bb.position(bc.limit());
|
||||
}
|
||||
if ((cb == null) || (cb.array() != cc.array())) {
|
||||
// Create a new char buffer if anything changed
|
||||
cb = CharBuffer.wrap(cc.array(), cc.arrayOffset() + cc.position(), cc.remaining());
|
||||
} else {
|
||||
// Initialize the char buffer
|
||||
cb.limit(cc.limit());
|
||||
cb.position(cc.position());
|
||||
}
|
||||
CoderResult result = null;
|
||||
// Parse leftover if any are present
|
||||
if (leftovers.position() > 0) {
|
||||
int pos = bb.position();
|
||||
// Loop until one char is encoded or there is a encoder error
|
||||
do {
|
||||
leftovers.put(cc.get());
|
||||
leftovers.flip();
|
||||
result = encoder.encode(leftovers, bb, false);
|
||||
leftovers.position(leftovers.limit());
|
||||
leftovers.limit(leftovers.array().length);
|
||||
} while (result.isUnderflow() && (bb.position() == pos));
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
}
|
||||
cb.position(cc.position());
|
||||
leftovers.position(0);
|
||||
}
|
||||
// Do the decoding and get the results into the byte chunk and the char
|
||||
// chunk
|
||||
result = encoder.encode(cb, bb, false);
|
||||
if (result.isError() || result.isMalformed()) {
|
||||
result.throwException();
|
||||
} else if (result.isOverflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.limit(bb.position());
|
||||
cc.position(cb.position());
|
||||
} else if (result.isUnderflow()) {
|
||||
// Propagate current positions to the byte chunk and char chunk
|
||||
bc.limit(bb.position());
|
||||
cc.position(cb.position());
|
||||
// Put leftovers in the leftovers char buffer
|
||||
if (cc.remaining() > 0) {
|
||||
leftovers.limit(leftovers.array().length);
|
||||
leftovers.position(cc.remaining());
|
||||
cc.get(leftovers.array(), 0, cc.remaining());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Charset getCharset() {
|
||||
return encoder.charset();
|
||||
}
|
||||
}
|
||||
662
java/org/apache/tomcat/util/buf/CharChunk.java
Normal file
662
java/org/apache/tomcat/util/buf/CharChunk.java
Normal file
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Utilities to manipulate char chunks. While String is the easiest way to
|
||||
* manipulate chars ( search, substrings, etc), it is known to not be the most
|
||||
* efficient solution - Strings are designed as immutable and secure objects.
|
||||
*
|
||||
* @author dac@sun.com
|
||||
* @author James Todd [gonzo@sun.com]
|
||||
* @author Costin Manolache
|
||||
* @author Remy Maucherat
|
||||
*/
|
||||
public final class CharChunk extends AbstractChunk implements CharSequence {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Input interface, used when the buffer is empty.
|
||||
*/
|
||||
public static interface CharInputChannel {
|
||||
|
||||
/**
|
||||
* Read new characters.
|
||||
*
|
||||
* @return The number of characters read
|
||||
*
|
||||
* @throws IOException If an I/O error occurs during reading
|
||||
*/
|
||||
public int realReadChars() throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* When we need more space we'll either grow the buffer ( up to the limit )
|
||||
* or send it to a channel.
|
||||
*/
|
||||
public static interface CharOutputChannel {
|
||||
|
||||
/**
|
||||
* Send the bytes ( usually the internal conversion buffer ). Expect 8k
|
||||
* output if the buffer is full.
|
||||
*
|
||||
* @param buf characters that will be written
|
||||
* @param off offset in the characters array
|
||||
* @param len length that will be written
|
||||
* @throws IOException If an I/O occurs while writing the characters
|
||||
*/
|
||||
public void realWriteChars(char buf[], int off, int len) throws IOException;
|
||||
}
|
||||
|
||||
// --------------------
|
||||
|
||||
// char[]
|
||||
private char[] buff;
|
||||
|
||||
// transient as serialization is primarily for values via, e.g. JMX
|
||||
private transient CharInputChannel in = null;
|
||||
private transient CharOutputChannel out = null;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new, uninitialized CharChunk object.
|
||||
*/
|
||||
public CharChunk() {
|
||||
}
|
||||
|
||||
|
||||
public CharChunk(int initial) {
|
||||
allocate(initial, -1);
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Setup --------------------
|
||||
|
||||
public void allocate(int initial, int limit) {
|
||||
if (buff == null || buff.length < initial) {
|
||||
buff = new char[initial];
|
||||
}
|
||||
setLimit(limit);
|
||||
start = 0;
|
||||
end = 0;
|
||||
isSet = true;
|
||||
hasHashCode = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the buffer to the specified subarray of characters.
|
||||
*
|
||||
* @param c the characters
|
||||
* @param off the start offset of the characters
|
||||
* @param len the length of the characters
|
||||
*/
|
||||
public void setChars(char[] c, int off, int len) {
|
||||
buff = c;
|
||||
start = off;
|
||||
end = start + len;
|
||||
isSet = true;
|
||||
hasHashCode = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the buffer.
|
||||
*/
|
||||
public char[] getChars() {
|
||||
return getBuffer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the buffer.
|
||||
*/
|
||||
public char[] getBuffer() {
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the buffer is empty, read the data from the input channel.
|
||||
*
|
||||
* @param in The input channel
|
||||
*/
|
||||
public void setCharInputChannel(CharInputChannel in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the buffer is full, write the data to the output channel. Also used
|
||||
* when large amount of data is appended. If not set, the buffer will grow
|
||||
* to the limit.
|
||||
*
|
||||
* @param out The output channel
|
||||
*/
|
||||
public void setCharOutputChannel(CharOutputChannel out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Adding data to the buffer --------------------
|
||||
|
||||
public void append(char c) throws IOException {
|
||||
makeSpace(1);
|
||||
int limit = getLimitInternal();
|
||||
|
||||
// couldn't make space
|
||||
if (end >= limit) {
|
||||
flushBuffer();
|
||||
}
|
||||
buff[end++] = c;
|
||||
}
|
||||
|
||||
|
||||
public void append(CharChunk src) throws IOException {
|
||||
append(src.getBuffer(), src.getOffset(), src.getLength());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add data to the buffer.
|
||||
*
|
||||
* @param src Char array
|
||||
* @param off Offset
|
||||
* @param len Length
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void append(char src[], int off, int len) throws IOException {
|
||||
// will grow, up to limit
|
||||
makeSpace(len);
|
||||
int limit = getLimitInternal();
|
||||
|
||||
// Optimize on a common case.
|
||||
// If the buffer is empty and the source is going to fill up all the
|
||||
// space in buffer, may as well write it directly to the output,
|
||||
// and avoid an extra copy
|
||||
if (len == limit && end == start && out != null) {
|
||||
out.realWriteChars(src, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we are below the limit
|
||||
if (len <= limit - end) {
|
||||
System.arraycopy(src, off, buff, end, len);
|
||||
end += len;
|
||||
return;
|
||||
}
|
||||
|
||||
// Need more space than we can afford, need to flush buffer.
|
||||
|
||||
// The buffer is already at (or bigger than) limit.
|
||||
|
||||
// Optimization:
|
||||
// If len-avail < length (i.e. after we fill the buffer with what we
|
||||
// can, the remaining will fit in the buffer) we'll just copy the first
|
||||
// part, flush, then copy the second part - 1 write and still have some
|
||||
// space for more. We'll still have 2 writes, but we write more on the first.
|
||||
|
||||
if (len + end < 2 * limit) {
|
||||
/*
|
||||
* If the request length exceeds the size of the output buffer,
|
||||
* flush the output buffer and then write the data directly. We
|
||||
* can't avoid 2 writes, but we can write more on the second
|
||||
*/
|
||||
int avail = limit - end;
|
||||
System.arraycopy(src, off, buff, end, avail);
|
||||
end += avail;
|
||||
|
||||
flushBuffer();
|
||||
|
||||
System.arraycopy(src, off + avail, buff, end, len - avail);
|
||||
end += len - avail;
|
||||
|
||||
} else { // len > buf.length + avail
|
||||
// long write - flush the buffer and write the rest
|
||||
// directly from source
|
||||
flushBuffer();
|
||||
|
||||
out.realWriteChars(src, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append a string to the buffer.
|
||||
*
|
||||
* @param s The string
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void append(String s) throws IOException {
|
||||
append(s, 0, s.length());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append a string to the buffer.
|
||||
*
|
||||
* @param s The string
|
||||
* @param off Offset
|
||||
* @param len Length
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void append(String s, int off, int len) throws IOException {
|
||||
if (s == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// will grow, up to limit
|
||||
makeSpace(len);
|
||||
int limit = getLimitInternal();
|
||||
|
||||
int sOff = off;
|
||||
int sEnd = off + len;
|
||||
while (sOff < sEnd) {
|
||||
int d = min(limit - end, sEnd - sOff);
|
||||
s.getChars(sOff, sOff + d, buff, end);
|
||||
sOff += d;
|
||||
end += d;
|
||||
if (end >= limit) {
|
||||
flushBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Removing data from the buffer --------------------
|
||||
|
||||
public int substract() throws IOException {
|
||||
if (checkEof()) {
|
||||
return -1;
|
||||
}
|
||||
return buff[start++];
|
||||
}
|
||||
|
||||
|
||||
public int substract(char dest[], int off, int len) throws IOException {
|
||||
if (checkEof()) {
|
||||
return -1;
|
||||
}
|
||||
int n = len;
|
||||
if (len > getLength()) {
|
||||
n = getLength();
|
||||
}
|
||||
System.arraycopy(buff, start, dest, off, n);
|
||||
start += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkEof() throws IOException {
|
||||
if ((end - start) == 0) {
|
||||
if (in == null) {
|
||||
return true;
|
||||
}
|
||||
int n = in.realReadChars();
|
||||
if (n < 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the buffer to the sink. Called by append() when the limit is
|
||||
* reached. You can also call it explicitly to force the data to be written.
|
||||
*
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void flushBuffer() throws IOException {
|
||||
// assert out!=null
|
||||
if (out == null) {
|
||||
throw new IOException("Buffer overflow, no sink " + getLimit() + " " + buff.length);
|
||||
}
|
||||
out.realWriteChars(buff, start, end - start);
|
||||
end = start;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make space for len chars. If len is small, allocate a reserve space too.
|
||||
* Never grow bigger than the limit or {@link AbstractChunk#ARRAY_MAX_SIZE}.
|
||||
*
|
||||
* @param count The size
|
||||
*/
|
||||
public void makeSpace(int count) {
|
||||
char[] tmp = null;
|
||||
|
||||
int limit = getLimitInternal();
|
||||
|
||||
long newSize;
|
||||
long desiredSize = end + count;
|
||||
|
||||
// Can't grow above the limit
|
||||
if (desiredSize > limit) {
|
||||
desiredSize = limit;
|
||||
}
|
||||
|
||||
if (buff == null) {
|
||||
if (desiredSize < 256) {
|
||||
desiredSize = 256; // take a minimum
|
||||
}
|
||||
buff = new char[(int) desiredSize];
|
||||
}
|
||||
|
||||
// limit < buf.length (the buffer is already big)
|
||||
// or we already have space XXX
|
||||
if (desiredSize <= buff.length) {
|
||||
return;
|
||||
}
|
||||
// grow in larger chunks
|
||||
if (desiredSize < 2L * buff.length) {
|
||||
newSize = buff.length * 2L;
|
||||
} else {
|
||||
newSize = buff.length * 2L + count;
|
||||
}
|
||||
|
||||
if (newSize > limit) {
|
||||
newSize = limit;
|
||||
}
|
||||
tmp = new char[(int) newSize];
|
||||
|
||||
// Some calling code assumes buffer will not be compacted
|
||||
System.arraycopy(buff, 0, tmp, 0, end);
|
||||
buff = tmp;
|
||||
tmp = null;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Conversion and getters --------------------
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (null == buff) {
|
||||
return null;
|
||||
} else if (end - start == 0) {
|
||||
return "";
|
||||
}
|
||||
return StringCache.toString(this);
|
||||
}
|
||||
|
||||
|
||||
public String toStringInternal() {
|
||||
return new String(buff, start, end - start);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- equals --------------------
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof CharChunk) {
|
||||
return equals((CharChunk) obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the message bytes to the specified String object.
|
||||
*
|
||||
* @param s the String to compare
|
||||
* @return <code>true</code> if the comparison succeeded, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equals(String s) {
|
||||
char[] c = buff;
|
||||
int len = end - start;
|
||||
if (c == null || len != s.length()) {
|
||||
return false;
|
||||
}
|
||||
int off = start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (c[off++] != s.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the message bytes to the specified String object.
|
||||
*
|
||||
* @param s the String to compare
|
||||
* @return <code>true</code> if the comparison succeeded, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equalsIgnoreCase(String s) {
|
||||
char[] c = buff;
|
||||
int len = end - start;
|
||||
if (c == null || len != s.length()) {
|
||||
return false;
|
||||
}
|
||||
int off = start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (Ascii.toLower(c[off++]) != Ascii.toLower(s.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(CharChunk cc) {
|
||||
return equals(cc.getChars(), cc.getOffset(), cc.getLength());
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(char b2[], int off2, int len2) {
|
||||
char b1[] = buff;
|
||||
if (b1 == null && b2 == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int len = end - start;
|
||||
if (len != len2 || b1 == null || b2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int off1 = start;
|
||||
|
||||
while (len-- > 0) {
|
||||
if (b1[off1++] != b2[off2++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the message bytes starts with the specified
|
||||
* string.
|
||||
* @param s The string
|
||||
*/
|
||||
public boolean startsWith(String s) {
|
||||
char[] c = buff;
|
||||
int len = s.length();
|
||||
if (c == null || len > end - start) {
|
||||
return false;
|
||||
}
|
||||
int off = start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (c[off++] != s.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the buffer starts with the specified string.
|
||||
*
|
||||
* @param s the string
|
||||
* @param pos The position
|
||||
*
|
||||
* @return <code>true</code> if the start matches
|
||||
*/
|
||||
public boolean startsWithIgnoreCase(String s, int pos) {
|
||||
char[] c = buff;
|
||||
int len = s.length();
|
||||
if (c == null || len + pos > end - start) {
|
||||
return false;
|
||||
}
|
||||
int off = start + pos;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (Ascii.toLower(c[off++]) != Ascii.toLower(s.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the message bytes end with the specified
|
||||
* string.
|
||||
* @param s The string
|
||||
*/
|
||||
public boolean endsWith(String s) {
|
||||
char[] c = buff;
|
||||
int len = s.length();
|
||||
if (c == null || len > end - start) {
|
||||
return false;
|
||||
}
|
||||
int off = end - len;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (c[off++] != s.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int getBufferElement(int index) {
|
||||
return buff[index];
|
||||
}
|
||||
|
||||
|
||||
public int indexOf(char c) {
|
||||
return indexOf(c, start);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first instance of the given character in this CharChunk
|
||||
* starting at the specified char. If the character is not found, -1 is
|
||||
* returned. <br>
|
||||
*
|
||||
* @param c The character
|
||||
* @param starting The start position
|
||||
* @return The position of the first instance of the character or -1 if the
|
||||
* character is not found.
|
||||
*/
|
||||
public int indexOf(char c, int starting) {
|
||||
int ret = indexOf(buff, start + starting, end, c);
|
||||
return (ret >= start) ? ret - start : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first instance of the given character in the given char array
|
||||
* between the specified start and end. <br>
|
||||
*
|
||||
* @param chars The array to search
|
||||
* @param start The point to start searching from in the array
|
||||
* @param end The point to stop searching in the array
|
||||
* @param s The character to search for
|
||||
* @return The position of the first instance of the character or -1 if the
|
||||
* character is not found.
|
||||
*/
|
||||
public static int indexOf(char chars[], int start, int end, char s) {
|
||||
int offset = start;
|
||||
|
||||
while (offset < end) {
|
||||
char c = chars[offset];
|
||||
if (c == s) {
|
||||
return offset;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- utils
|
||||
private int min(int a, int b) {
|
||||
if (a < b) {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
// Char sequence impl
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return buff[index + start];
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
try {
|
||||
CharChunk result = (CharChunk) this.clone();
|
||||
result.setOffset(this.start + start);
|
||||
result.setEnd(this.start + end);
|
||||
return result;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// Cannot happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return end - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* NO-OP.
|
||||
*
|
||||
* @param optimizedWrite Ignored
|
||||
*
|
||||
* @deprecated Unused code. This is now a NO-OP and will be removed without
|
||||
* replacement in Tomcat 10.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setOptimizedWrite(boolean optimizedWrite) {
|
||||
}
|
||||
}
|
||||
230
java/org/apache/tomcat/util/buf/CharsetCache.java
Normal file
230
java/org/apache/tomcat/util/buf/CharsetCache.java
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class CharsetCache {
|
||||
|
||||
/* Note: Package private to enable testing without reflection */
|
||||
static final String[] INITIAL_CHARSETS = new String[] { "iso-8859-1", "utf-8" };
|
||||
|
||||
/*
|
||||
* Note: Package private to enable testing without reflection
|
||||
*/
|
||||
static final String[] LAZY_CHARSETS = new String[] {
|
||||
// Initial set from Oracle JDK 8 u192
|
||||
"037", "1006", "1025", "1026", "1046", "1047", "1089", "1097", "1098", "1112", "1122", "1123", "1124",
|
||||
"1140", "1141", "1142", "1143", "1144", "1145", "1146", "1147", "1148", "1149", "1166", "1364", "1381",
|
||||
"1383", "273", "277", "278", "280", "284", "285", "290", "297", "300", "33722", "420", "424", "437", "500",
|
||||
"5601", "646", "737", "775", "813", "834", "838", "850", "852", "855", "856", "857", "858", "860", "861",
|
||||
"862", "863", "864", "865", "866", "868", "869", "870", "871", "874", "875", "8859_13", "8859_15", "8859_2",
|
||||
"8859_3", "8859_4", "8859_5", "8859_6", "8859_7", "8859_8", "8859_9", "912", "913", "914", "915", "916",
|
||||
"918", "920", "921", "922", "923", "930", "933", "935", "937", "939", "942", "942c", "943", "943c", "948",
|
||||
"949", "949c", "950", "964", "970", "ansi-1251", "ansi_x3.4-1968", "ansi_x3.4-1986", "arabic", "ascii",
|
||||
"ascii7", "asmo-708", "big5", "big5-hkscs", "big5-hkscs", "big5-hkscs-2001", "big5-hkscs:unicode3.0",
|
||||
"big5_hkscs", "big5_hkscs_2001", "big5_solaris", "big5hk", "big5hk-2001", "big5hkscs", "big5hkscs-2001",
|
||||
"ccsid00858", "ccsid01140", "ccsid01141", "ccsid01142", "ccsid01143", "ccsid01144", "ccsid01145",
|
||||
"ccsid01146", "ccsid01147", "ccsid01148", "ccsid01149", "cesu-8", "cesu8", "cns11643", "compound_text",
|
||||
"cp-ar", "cp-gr", "cp-is", "cp00858", "cp01140", "cp01141", "cp01142", "cp01143", "cp01144", "cp01145",
|
||||
"cp01146", "cp01147", "cp01148", "cp01149", "cp037", "cp1006", "cp1025", "cp1026", "cp1046", "cp1047",
|
||||
"cp1089", "cp1097", "cp1098", "cp1112", "cp1122", "cp1123", "cp1124", "cp1140", "cp1141", "cp1142",
|
||||
"cp1143", "cp1144", "cp1145", "cp1146", "cp1147", "cp1148", "cp1149", "cp1166", "cp1250", "cp1251",
|
||||
"cp1252", "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258", "cp1364", "cp1381", "cp1383", "cp273",
|
||||
"cp277", "cp278", "cp280", "cp284", "cp285", "cp290", "cp297", "cp300", "cp33722", "cp367", "cp420",
|
||||
"cp424", "cp437", "cp500", "cp50220", "cp50221", "cp5346", "cp5347", "cp5348", "cp5349", "cp5350", "cp5353",
|
||||
"cp737", "cp775", "cp813", "cp833", "cp834", "cp838", "cp850", "cp852", "cp855", "cp856", "cp857", "cp858",
|
||||
"cp860", "cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp868", "cp869", "cp870", "cp871", "cp874",
|
||||
"cp875", "cp912", "cp913", "cp914", "cp915", "cp916", "cp918", "cp920", "cp921", "cp922", "cp923", "cp930",
|
||||
"cp933", "cp935", "cp936", "cp937", "cp939", "cp942", "cp942c", "cp943", "cp943c", "cp948", "cp949",
|
||||
"cp949c", "cp950", "cp964", "cp970", "cpibm284", "cpibm285", "cpibm297", "cpibm37", "cs-ebcdic-cp-ca",
|
||||
"cs-ebcdic-cp-nl", "cs-ebcdic-cp-us", "cs-ebcdic-cp-wt", "csascii", "csbig5", "cscesu-8", "cseuckr",
|
||||
"cseucpkdfmtjapanese", "cshalfwidthkatakana", "csibm037", "csibm278", "csibm284", "csibm285", "csibm290",
|
||||
"csibm297", "csibm420", "csibm424", "csibm500", "csibm857", "csibm860", "csibm861", "csibm862", "csibm863",
|
||||
"csibm864", "csibm865", "csibm866", "csibm868", "csibm869", "csibm870", "csibm871", "csiso153gost1976874",
|
||||
"csiso159jisx02121990", "csiso2022cn", "csiso2022jp", "csiso2022jp2", "csiso2022kr", "csiso87jisx0208",
|
||||
"csisolatin0", "csisolatin2", "csisolatin3", "csisolatin4", "csisolatin5", "csisolatin9",
|
||||
"csisolatinarabic", "csisolatincyrillic", "csisolatingreek", "csisolatinhebrew", "csjisencoding", "cskoi8r",
|
||||
"cspc850multilingual", "cspc862latinhebrew", "cspc8codepage437", "cspcp852", "cspcp855", "csshiftjis",
|
||||
"cswindows31j", "cyrillic", "default", "ebcdic-cp-ar1", "ebcdic-cp-ar2", "ebcdic-cp-bh", "ebcdic-cp-ca",
|
||||
"ebcdic-cp-ch", "ebcdic-cp-fr", "ebcdic-cp-gb", "ebcdic-cp-he", "ebcdic-cp-is", "ebcdic-cp-nl",
|
||||
"ebcdic-cp-roece", "ebcdic-cp-se", "ebcdic-cp-us", "ebcdic-cp-wt", "ebcdic-cp-yu", "ebcdic-de-273+euro",
|
||||
"ebcdic-dk-277+euro", "ebcdic-es-284+euro", "ebcdic-fi-278+euro", "ebcdic-fr-277+euro", "ebcdic-gb",
|
||||
"ebcdic-gb-285+euro", "ebcdic-international-500+euro", "ebcdic-it-280+euro", "ebcdic-jp-kana",
|
||||
"ebcdic-no-277+euro", "ebcdic-s-871+euro", "ebcdic-se-278+euro", "ebcdic-sv", "ebcdic-us-037+euro",
|
||||
"ecma-114", "ecma-118", "elot_928", "euc-cn", "euc-jp", "euc-jp-linux", "euc-kr", "euc-tw", "euc_cn",
|
||||
"euc_jp", "euc_jp_linux", "euc_jp_solaris", "euc_kr", "euc_tw", "euccn", "eucjis", "eucjp", "eucjp-open",
|
||||
"euckr", "euctw", "extended_unix_code_packed_format_for_japanese", "gb18030", "gb18030-2000", "gb2312",
|
||||
"gb2312", "gb2312-1980", "gb2312-80", "gbk", "greek", "greek8", "hebrew", "ibm-037", "ibm-1006", "ibm-1025",
|
||||
"ibm-1026", "ibm-1046", "ibm-1047", "ibm-1089", "ibm-1097", "ibm-1098", "ibm-1112", "ibm-1122", "ibm-1123",
|
||||
"ibm-1124", "ibm-1166", "ibm-1364", "ibm-1381", "ibm-1383", "ibm-273", "ibm-277", "ibm-278", "ibm-280",
|
||||
"ibm-284", "ibm-285", "ibm-290", "ibm-297", "ibm-300", "ibm-33722", "ibm-33722_vascii_vpua", "ibm-37",
|
||||
"ibm-420", "ibm-424", "ibm-437", "ibm-500", "ibm-5050", "ibm-737", "ibm-775", "ibm-813", "ibm-833",
|
||||
"ibm-834", "ibm-838", "ibm-850", "ibm-852", "ibm-855", "ibm-856", "ibm-857", "ibm-860", "ibm-861",
|
||||
"ibm-862", "ibm-863", "ibm-864", "ibm-865", "ibm-866", "ibm-868", "ibm-869", "ibm-870", "ibm-871",
|
||||
"ibm-874", "ibm-875", "ibm-912", "ibm-913", "ibm-914", "ibm-915", "ibm-916", "ibm-918", "ibm-920",
|
||||
"ibm-921", "ibm-922", "ibm-923", "ibm-930", "ibm-933", "ibm-935", "ibm-937", "ibm-939", "ibm-942",
|
||||
"ibm-942c", "ibm-943", "ibm-943c", "ibm-948", "ibm-949", "ibm-949c", "ibm-950", "ibm-964", "ibm-970",
|
||||
"ibm-euckr", "ibm-thai", "ibm00858", "ibm01140", "ibm01141", "ibm01142", "ibm01143", "ibm01144", "ibm01145",
|
||||
"ibm01146", "ibm01147", "ibm01148", "ibm01149", "ibm037", "ibm037", "ibm1006", "ibm1025", "ibm1026",
|
||||
"ibm1026", "ibm1046", "ibm1047", "ibm1089", "ibm1097", "ibm1098", "ibm1112", "ibm1122", "ibm1123",
|
||||
"ibm1124", "ibm1166", "ibm1364", "ibm1381", "ibm1383", "ibm273", "ibm273", "ibm277", "ibm277", "ibm278",
|
||||
"ibm278", "ibm280", "ibm280", "ibm284", "ibm284", "ibm285", "ibm285", "ibm290", "ibm290", "ibm297",
|
||||
"ibm297", "ibm300", "ibm33722", "ibm367", "ibm420", "ibm420", "ibm424", "ibm424", "ibm437", "ibm437",
|
||||
"ibm500", "ibm500", "ibm737", "ibm775", "ibm775", "ibm813", "ibm833", "ibm834", "ibm838", "ibm850",
|
||||
"ibm850", "ibm852", "ibm852", "ibm855", "ibm855", "ibm856", "ibm857", "ibm857", "ibm860", "ibm860",
|
||||
"ibm861", "ibm861", "ibm862", "ibm862", "ibm863", "ibm863", "ibm864", "ibm864", "ibm865", "ibm865",
|
||||
"ibm866", "ibm866", "ibm868", "ibm868", "ibm869", "ibm869", "ibm870", "ibm870", "ibm871", "ibm871",
|
||||
"ibm874", "ibm875", "ibm912", "ibm913", "ibm914", "ibm915", "ibm916", "ibm918", "ibm920", "ibm921",
|
||||
"ibm922", "ibm923", "ibm930", "ibm933", "ibm935", "ibm937", "ibm939", "ibm942", "ibm942c", "ibm943",
|
||||
"ibm943c", "ibm948", "ibm949", "ibm949c", "ibm950", "ibm964", "ibm970", "iscii", "iscii91",
|
||||
"iso-10646-ucs-2", "iso-2022-cn", "iso-2022-cn-cns", "iso-2022-cn-gb", "iso-2022-jp", "iso-2022-jp-2",
|
||||
"iso-2022-kr", "iso-8859-11", "iso-8859-13", "iso-8859-15", "iso-8859-15", "iso-8859-2", "iso-8859-3",
|
||||
"iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-9", "iso-ir-101",
|
||||
"iso-ir-109", "iso-ir-110", "iso-ir-126", "iso-ir-127", "iso-ir-138", "iso-ir-144", "iso-ir-148",
|
||||
"iso-ir-153", "iso-ir-159", "iso-ir-6", "iso-ir-87", "iso2022cn", "iso2022cn_cns", "iso2022cn_gb",
|
||||
"iso2022jp", "iso2022jp2", "iso2022kr", "iso646-us", "iso8859-13", "iso8859-15", "iso8859-2", "iso8859-3",
|
||||
"iso8859-4", "iso8859-5", "iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso8859_11", "iso8859_13",
|
||||
"iso8859_15", "iso8859_15_fdis", "iso8859_2", "iso8859_3", "iso8859_4", "iso8859_5", "iso8859_6",
|
||||
"iso8859_7", "iso8859_8", "iso8859_9", "iso_646.irv:1983", "iso_646.irv:1991", "iso_8859-13", "iso_8859-15",
|
||||
"iso_8859-2", "iso_8859-2:1987", "iso_8859-3", "iso_8859-3:1988", "iso_8859-4", "iso_8859-4:1988",
|
||||
"iso_8859-5", "iso_8859-5:1988", "iso_8859-6", "iso_8859-6:1987", "iso_8859-7", "iso_8859-7:1987",
|
||||
"iso_8859-8", "iso_8859-8:1988", "iso_8859-9", "iso_8859-9:1989", "jis", "jis0201", "jis0208", "jis0212",
|
||||
"jis_c6226-1983", "jis_encoding", "jis_x0201", "jis_x0201", "jis_x0208-1983", "jis_x0212-1990",
|
||||
"jis_x0212-1990", "jisautodetect", "johab", "koi8", "koi8-r", "koi8-u", "koi8_r", "koi8_u",
|
||||
"ks_c_5601-1987", "ksc5601", "ksc5601-1987", "ksc5601-1992", "ksc5601_1987", "ksc5601_1992", "ksc_5601",
|
||||
"l2", "l3", "l4", "l5", "l9", "latin0", "latin2", "latin3", "latin4", "latin5", "latin9", "macarabic",
|
||||
"maccentraleurope", "maccroatian", "maccyrillic", "macdingbat", "macgreek", "machebrew", "maciceland",
|
||||
"macroman", "macromania", "macsymbol", "macthai", "macturkish", "macukraine", "ms-874", "ms1361", "ms50220",
|
||||
"ms50221", "ms874", "ms932", "ms936", "ms949", "ms950", "ms950_hkscs", "ms950_hkscs_xp", "ms_936", "ms_949",
|
||||
"ms_kanji", "pc-multilingual-850+euro", "pck", "shift-jis", "shift_jis", "shift_jis", "sjis",
|
||||
"st_sev_358-88", "sun_eu_greek", "tis-620", "tis620", "tis620.2533", "unicode", "unicodebig",
|
||||
"unicodebigunmarked", "unicodelittle", "unicodelittleunmarked", "us", "us-ascii", "utf-16", "utf-16be",
|
||||
"utf-16le", "utf-32", "utf-32be", "utf-32be-bom", "utf-32le", "utf-32le-bom", "utf16", "utf32", "utf_16",
|
||||
"utf_16be", "utf_16le", "utf_32", "utf_32be", "utf_32be_bom", "utf_32le", "utf_32le_bom", "windows-1250",
|
||||
"windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", "windows-1256",
|
||||
"windows-1257", "windows-1258", "windows-31j", "windows-437", "windows-874", "windows-932", "windows-936",
|
||||
"windows-949", "windows-950", "windows-iso2022jp", "windows949", "x-big5-hkscs-2001", "x-big5-solaris",
|
||||
"x-compound-text", "x-compound_text", "x-euc-cn", "x-euc-jp", "x-euc-jp-linux", "x-euc-tw", "x-eucjp",
|
||||
"x-eucjp-open", "x-ibm1006", "x-ibm1025", "x-ibm1046", "x-ibm1097", "x-ibm1098", "x-ibm1112", "x-ibm1122",
|
||||
"x-ibm1123", "x-ibm1124", "x-ibm1166", "x-ibm1364", "x-ibm1381", "x-ibm1383", "x-ibm300", "x-ibm33722",
|
||||
"x-ibm737", "x-ibm833", "x-ibm834", "x-ibm856", "x-ibm874", "x-ibm875", "x-ibm921", "x-ibm922", "x-ibm930",
|
||||
"x-ibm933", "x-ibm935", "x-ibm937", "x-ibm939", "x-ibm942", "x-ibm942c", "x-ibm943", "x-ibm943c",
|
||||
"x-ibm948", "x-ibm949", "x-ibm949c", "x-ibm950", "x-ibm964", "x-ibm970", "x-iscii91", "x-iso-2022-cn-cns",
|
||||
"x-iso-2022-cn-gb", "x-iso-8859-11", "x-jis0208", "x-jisautodetect", "x-johab", "x-macarabic",
|
||||
"x-maccentraleurope", "x-maccroatian", "x-maccyrillic", "x-macdingbat", "x-macgreek", "x-machebrew",
|
||||
"x-maciceland", "x-macroman", "x-macromania", "x-macsymbol", "x-macthai", "x-macturkish", "x-macukraine",
|
||||
"x-ms932_0213", "x-ms950-hkscs", "x-ms950-hkscs-xp", "x-mswin-936", "x-pck", "x-sjis", "x-sjis_0213",
|
||||
"x-utf-16be", "x-utf-16le", "x-utf-16le-bom", "x-utf-32be", "x-utf-32be-bom", "x-utf-32le",
|
||||
"x-utf-32le-bom", "x-windows-50220", "x-windows-50221", "x-windows-874", "x-windows-949", "x-windows-950",
|
||||
"x-windows-iso2022jp", "x0201", "x0208", "x0212", "x11-compound_text",
|
||||
// Added from Oracle JDK 10.0.2
|
||||
"csiso885915", "csiso885916", "iso-8859-16", "iso-ir-226", "iso_8859-16", "iso_8859-16:2001", "l10",
|
||||
"latin-9", "latin10", "ms932-0213", "ms932:2004", "ms932_0213", "shift_jis:2004", "shift_jis_0213:2004",
|
||||
"sjis-0213", "sjis:2004", "sjis_0213", "sjis_0213:2004", "windows-932-0213", "windows-932:2004",
|
||||
// Added from OpenJDK 11.0.1
|
||||
"932", "cp932", "cpeuccn", "ibm-1252", "ibm-932", "ibm-euccn", "ibm1252", "ibm932", "ibmeuccn", "x-ibm932",
|
||||
// Added from OpenJDK 12 ea28
|
||||
"1129", "cp1129", "ibm-1129", "ibm-euctw", "ibm1129", "x-ibm1129",
|
||||
// Added from OpenJDK 13 ea15
|
||||
"29626c", "833", "cp29626c", "ibm-1140", "ibm-1141", "ibm-1142", "ibm-1143", "ibm-1144", "ibm-1145",
|
||||
"ibm-1146", "ibm-1147", "ibm-1148", "ibm-1149", "ibm-29626c", "ibm-858", "ibm-eucjp", "ibm1140", "ibm1141",
|
||||
"ibm1142", "ibm1143", "ibm1144", "ibm1145", "ibm1146", "ibm1147", "ibm1148", "ibm1149", "ibm29626c",
|
||||
"ibm858", "x-ibm29626c",
|
||||
// Added from HPE JVM 1.8.0.17-hp-ux
|
||||
"cp1051", "cp1386", "cshproman8", "hp-roman8", "ibm-1051", "r8", "roman8", "roman9"
|
||||
};
|
||||
|
||||
private static final Charset DUMMY_CHARSET = new DummyCharset("Dummy", null);
|
||||
|
||||
private ConcurrentMap<String,Charset> cache = new ConcurrentHashMap<>();
|
||||
|
||||
public CharsetCache() {
|
||||
// Pre-populate the cache
|
||||
for (String charsetName : INITIAL_CHARSETS) {
|
||||
Charset charset = Charset.forName(charsetName);
|
||||
addToCache(charsetName, charset);
|
||||
}
|
||||
|
||||
for (String charsetName : LAZY_CHARSETS) {
|
||||
addToCache(charsetName, DUMMY_CHARSET);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addToCache(String name, Charset charset) {
|
||||
cache.put(name, charset);
|
||||
for (String alias : charset.aliases()) {
|
||||
cache.put(alias.toLowerCase(Locale.ENGLISH), charset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Charset getCharset(String charsetName) {
|
||||
String lcCharsetName = charsetName.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
Charset result = cache.get(lcCharsetName);
|
||||
|
||||
if (result == DUMMY_CHARSET) {
|
||||
// Name is known but the Charset is not in the cache
|
||||
Charset charset = Charset.forName(lcCharsetName);
|
||||
if (charset == null) {
|
||||
// Charset not available in this JVM - remove cache entry
|
||||
cache.remove(lcCharsetName);
|
||||
result = null;
|
||||
} else {
|
||||
// Charset is available - populate cache entry
|
||||
addToCache(lcCharsetName, charset);
|
||||
result = charset;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Placeholder Charset implementation for entries that will be loaded lazily
|
||||
* into the cache.
|
||||
*/
|
||||
private static class DummyCharset extends Charset {
|
||||
|
||||
protected DummyCharset(String canonicalName, String[] aliases) {
|
||||
super(canonicalName, aliases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Charset cs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharsetDecoder newDecoder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharsetEncoder newEncoder() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
java/org/apache/tomcat/util/buf/Constants.java
Normal file
26
java/org/apache/tomcat/util/buf/Constants.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
/**
|
||||
* String constants for the file package.
|
||||
*/
|
||||
public final class Constants {
|
||||
|
||||
public static final String Package = "org.apache.tomcat.util.buf";
|
||||
|
||||
}
|
||||
118
java/org/apache/tomcat/util/buf/HexUtils.java
Normal file
118
java/org/apache/tomcat/util/buf/HexUtils.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* Tables useful when converting byte arrays to and from strings of hexadecimal
|
||||
* digits.
|
||||
* Code from Ajp11, from Apache's JServ.
|
||||
*
|
||||
* @author Craig R. McClanahan
|
||||
*/
|
||||
public final class HexUtils {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(Constants.Package);
|
||||
|
||||
// -------------------------------------------------------------- Constants
|
||||
|
||||
/**
|
||||
* Table for HEX to DEC byte translation.
|
||||
*/
|
||||
private static final int[] DEC = {
|
||||
00, 01, 02, 03, 04, 05, 06, 07, 8, 9, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Table for DEC to HEX byte translation.
|
||||
*/
|
||||
private static final byte[] HEX =
|
||||
{ (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
|
||||
(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
|
||||
(byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
|
||||
|
||||
|
||||
/**
|
||||
* Table for byte to hex string translation.
|
||||
*/
|
||||
private static final char[] hex = "0123456789abcdef".toCharArray();
|
||||
|
||||
|
||||
// --------------------------------------------------------- Static Methods
|
||||
|
||||
public static int getDec(int index) {
|
||||
// Fast for correct values, slower for incorrect ones
|
||||
try {
|
||||
return DEC[index - '0'];
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte getHex(int index) {
|
||||
return HEX[index];
|
||||
}
|
||||
|
||||
|
||||
public static String toHexString(byte[] bytes) {
|
||||
if (null == bytes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(bytes.length << 1);
|
||||
|
||||
for(int i = 0; i < bytes.length; ++i) {
|
||||
sb.append(hex[(bytes[i] & 0xf0) >> 4])
|
||||
.append(hex[(bytes[i] & 0x0f)])
|
||||
;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] fromHexString(String input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((input.length() & 1) == 1) {
|
||||
// Odd number of characters
|
||||
throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.oddDigits"));
|
||||
}
|
||||
|
||||
char[] inputChars = input.toCharArray();
|
||||
byte[] result = new byte[input.length() >> 1];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
int upperNibble = getDec(inputChars[2*i]);
|
||||
int lowerNibble = getDec(inputChars[2*i + 1]);
|
||||
if (upperNibble < 0 || lowerNibble < 0) {
|
||||
// Non hex character
|
||||
throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.nonHex"));
|
||||
}
|
||||
result[i] = (byte) ((upperNibble << 4) + lowerNibble);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
34
java/org/apache/tomcat/util/buf/LocalStrings.properties
Normal file
34
java/org/apache/tomcat/util/buf/LocalStrings.properties
Normal file
@@ -0,0 +1,34 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
asn1Parser.lengthInvalid=Invalid length [{0}] bytes reported when the input data length is [{1}] bytes
|
||||
asn1Parser.tagMismatch=Expected to find value [{0}] but found value [{1}]
|
||||
|
||||
b2cConverter.unknownEncoding=The character encoding [{0}] is not supported
|
||||
|
||||
byteBufferUtils.cleaner=Cannot use direct ByteBuffer cleaner, memory leaking may occur
|
||||
|
||||
c2bConverter.recycleFailed=Failed to recycle the C2B Converter. Creating new BufferedWriter, WriteConvertor and IntermediateOutputStream.
|
||||
|
||||
hexUtils.fromHex.nonHex=The input must consist only of hex digits
|
||||
hexUtils.fromHex.oddDigits=The input must consist of an even number of hex digits
|
||||
|
||||
uDecoder.eof=End of file (EOF)
|
||||
uDecoder.noSlash=The encoded slash character is not allowed
|
||||
uDecoder.urlDecode.conversionError=Failed to decode [{0}] using character set [{1}]
|
||||
uDecoder.urlDecode.missingDigit=Failed to decode [{0}] because the % character must be followed by two hexademical digits
|
||||
uDecoder.urlDecode.uee=Unable to URL decode the specified input since the encoding [{0}] is not supported.
|
||||
|
||||
udecoder.urlDecode.iae=It is practical to %nn decode a byte array since how the %nn is encoded will vary by character set
|
||||
16
java/org/apache/tomcat/util/buf/LocalStrings_de.properties
Normal file
16
java/org/apache/tomcat/util/buf/LocalStrings_de.properties
Normal file
@@ -0,0 +1,16 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
uDecoder.convertHexDigit.notHex=[{0}] ist keine hexadezimale Ziffer
|
||||
16
java/org/apache/tomcat/util/buf/LocalStrings_es.properties
Normal file
16
java/org/apache/tomcat/util/buf/LocalStrings_es.properties
Normal file
@@ -0,0 +1,16 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
b2cConverter.unknownEncoding=La codificación de carácter [{0}] no está soportada
|
||||
29
java/org/apache/tomcat/util/buf/LocalStrings_fr.properties
Normal file
29
java/org/apache/tomcat/util/buf/LocalStrings_fr.properties
Normal file
@@ -0,0 +1,29 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
asn1Parser.lengthInvalid=Une longueur d''octets invalide [{0}] a été rapportée alors que la longueur de données en entrée est de [{1}] octets
|
||||
asn1Parser.tagMismatch=La valeur [{0}] était attendue mais la valeur [{1}] a été rencontrée
|
||||
|
||||
b2cConverter.unknownEncoding=L''encodage de caractères [{0}] n''est pas supporté
|
||||
|
||||
byteBufferUtils.cleaner=Impossible d'utiliser le nettoyeur de ByteBuffers directs, une fuite de mémoire peut se produire
|
||||
|
||||
hexUtils.fromHex.nonHex=L'entrée doit être uniquement des chiffres héxadécimaux
|
||||
hexUtils.fromHex.oddDigits=L'entrée doit contenir un nombre pair de chiffres héxadécimaux
|
||||
|
||||
uDecoder.eof=Fin de fichier (EOF)
|
||||
uDecoder.noSlash=Un caractère slash encodé n'est pas autorisé
|
||||
uDecoder.urlDecode.conversionError=Echec de décodage [{0}] en utilisant le jeu de caractères [{1}]
|
||||
uDecoder.urlDecode.missingDigit=Impossible de décoder [{0}] parce que le caractère % doit être suivi de deux chiffres héxadécimaux
|
||||
26
java/org/apache/tomcat/util/buf/LocalStrings_ja.properties
Normal file
26
java/org/apache/tomcat/util/buf/LocalStrings_ja.properties
Normal file
@@ -0,0 +1,26 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
b2cConverter.unknownEncoding=文字エンコーディング [{0}] は未対応です。
|
||||
|
||||
byteBufferUtils.cleaner=直接ByteBufferクリーナーを使用することはできません、メモリリークが発生する可能性があります。
|
||||
|
||||
hexUtils.fromHex.nonHex=入力は16進数でなければなりません
|
||||
hexUtils.fromHex.oddDigits=入力は、偶数の16進数で構成する必要があります。
|
||||
|
||||
uDecoder.eof=予期せぬ場所で終端に達しました。
|
||||
uDecoder.noSlash="/" を符号化して含めることはできません。
|
||||
uDecoder.urlDecode.conversionError=文字セット[{1}]を使用して[{0}]のデコードに失敗しました
|
||||
uDecoder.urlDecode.missingDigit=%文字の後ろに2つの16進数字が続く必要があるため、[{0}]のデコードに失敗しました。
|
||||
29
java/org/apache/tomcat/util/buf/LocalStrings_ko.properties
Normal file
29
java/org/apache/tomcat/util/buf/LocalStrings_ko.properties
Normal file
@@ -0,0 +1,29 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
asn1Parser.lengthInvalid=입력 데이터의 바이트 길이가 [{1}]인데, 유효하지 않은 바이트 길이 [{0}](이)가 보고되었습니다.
|
||||
asn1Parser.tagMismatch=[{0}] 값이 기대 되었는데, [{1}] 값이 발견되었습니다.
|
||||
|
||||
b2cConverter.unknownEncoding=문자 인코딩 [{0}]은(는) 지원되지 않습니다.
|
||||
|
||||
byteBufferUtils.cleaner=직접적인 ByteBuffer cleaner를 사용할 수 없습니다. 메모리 누수가 발생할 수 있습니다.
|
||||
|
||||
hexUtils.fromHex.nonHex=입력은 오직 16진수 숫자로만 이루어져야 합니다.
|
||||
hexUtils.fromHex.oddDigits=입력은 반드시 짝수 개의 16진수 숫자들로 이루어져야 합니다.
|
||||
|
||||
uDecoder.eof=파일의 끝 (EOF)
|
||||
uDecoder.noSlash=인코딩된 슬래시 문자는 허용되지 않습니다.
|
||||
uDecoder.urlDecode.conversionError=문자셋 [{1}]을(를) 사용하여 [{0}]을(를) 디코드하지 못했습니다.
|
||||
uDecoder.urlDecode.missingDigit=% 문자 뒤에 두 개의 16진수 숫자들이 이어져야 하기 때문에, [{0}]을(를) 디코드하지 못했습니다.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
asn1Parser.lengthInvalid=无效长度 [{0}]字节报告,但是输入数据的长度是 [{1}]字节
|
||||
asn1Parser.tagMismatch=期望找到值 [{0}]但是却找到值 [{1}]
|
||||
|
||||
hexUtils.fromHex.nonHex=输入只能由十六进制数字组成
|
||||
|
||||
uDecoder.urlDecode.conversionError=使用编码[{1}]解码[{0}]失败
|
||||
578
java/org/apache/tomcat/util/buf/MessageBytes.java
Normal file
578
java/org/apache/tomcat/util/buf/MessageBytes.java
Normal file
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class is used to represent a subarray of bytes in an HTTP message.
|
||||
* It represents all request/response elements. The byte/char conversions are
|
||||
* delayed and cached. Everything is recyclable.
|
||||
*
|
||||
* The object can represent a byte[], a char[], or a (sub) String. All
|
||||
* operations can be made in case sensitive mode or not.
|
||||
*
|
||||
* @author dac@eng.sun.com
|
||||
* @author James Todd [gonzo@eng.sun.com]
|
||||
* @author Costin Manolache
|
||||
*/
|
||||
public final class MessageBytes implements Cloneable, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// primary type ( whatever is set as original value )
|
||||
private int type = T_NULL;
|
||||
|
||||
public static final int T_NULL = 0;
|
||||
/** getType() is T_STR if the the object used to create the MessageBytes
|
||||
was a String */
|
||||
public static final int T_STR = 1;
|
||||
/** getType() is T_BYTES if the the object used to create the MessageBytes
|
||||
was a byte[] */
|
||||
public static final int T_BYTES = 2;
|
||||
/** getType() is T_CHARS if the the object used to create the MessageBytes
|
||||
was a char[] */
|
||||
public static final int T_CHARS = 3;
|
||||
|
||||
private int hashCode=0;
|
||||
// did we compute the hashcode ?
|
||||
private boolean hasHashCode=false;
|
||||
|
||||
// Internal objects to represent array + offset, and specific methods
|
||||
private final ByteChunk byteC=new ByteChunk();
|
||||
private final CharChunk charC=new CharChunk();
|
||||
|
||||
// String
|
||||
private String strValue;
|
||||
// true if a String value was computed. Probably not needed,
|
||||
// strValue!=null is the same
|
||||
private boolean hasStrValue=false;
|
||||
|
||||
/**
|
||||
* Creates a new, uninitialized MessageBytes object.
|
||||
* Use static newInstance() in order to allow
|
||||
* future hooks.
|
||||
*/
|
||||
private MessageBytes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new MessageBytes instance.
|
||||
* @return the instance
|
||||
*/
|
||||
public static MessageBytes newInstance() {
|
||||
return factory.newInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
return byteC.isNull() && charC.isNull() && !hasStrValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the message bytes to an uninitialized (NULL) state.
|
||||
*/
|
||||
public void recycle() {
|
||||
type=T_NULL;
|
||||
byteC.recycle();
|
||||
charC.recycle();
|
||||
|
||||
strValue=null;
|
||||
|
||||
hasStrValue=false;
|
||||
hasHashCode=false;
|
||||
hasLongValue=false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the content to the specified subarray of bytes.
|
||||
*
|
||||
* @param b the bytes
|
||||
* @param off the start offset of the bytes
|
||||
* @param len the length of the bytes
|
||||
*/
|
||||
public void setBytes(byte[] b, int off, int len) {
|
||||
byteC.setBytes( b, off, len );
|
||||
type=T_BYTES;
|
||||
hasStrValue=false;
|
||||
hasHashCode=false;
|
||||
hasLongValue=false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content to be a char[]
|
||||
*
|
||||
* @param c the chars
|
||||
* @param off the start offset of the chars
|
||||
* @param len the length of the chars
|
||||
*/
|
||||
public void setChars( char[] c, int off, int len ) {
|
||||
charC.setChars( c, off, len );
|
||||
type=T_CHARS;
|
||||
hasStrValue=false;
|
||||
hasHashCode=false;
|
||||
hasLongValue=false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content to be a string
|
||||
* @param s The string
|
||||
*/
|
||||
public void setString( String s ) {
|
||||
strValue=s;
|
||||
hasHashCode=false;
|
||||
hasLongValue=false;
|
||||
if (s == null) {
|
||||
hasStrValue=false;
|
||||
type=T_NULL;
|
||||
} else {
|
||||
hasStrValue=true;
|
||||
type=T_STR;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- Conversion and getters --------------------
|
||||
|
||||
/**
|
||||
* Compute the string value.
|
||||
* @return the string
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (hasStrValue) {
|
||||
return strValue;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case T_CHARS:
|
||||
strValue = charC.toString();
|
||||
hasStrValue = true;
|
||||
return strValue;
|
||||
case T_BYTES:
|
||||
strValue = byteC.toString();
|
||||
hasStrValue = true;
|
||||
return strValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
/**
|
||||
* Return the type of the original content. Can be
|
||||
* T_STR, T_BYTES, T_CHARS or T_NULL
|
||||
* @return the type
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte chunk, representing the byte[] and offset/length.
|
||||
* Valid only if T_BYTES or after a conversion was made.
|
||||
* @return the byte chunk
|
||||
*/
|
||||
public ByteChunk getByteChunk() {
|
||||
return byteC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the char chunk, representing the char[] and offset/length.
|
||||
* Valid only if T_CHARS or after a conversion was made.
|
||||
* @return the char chunk
|
||||
*/
|
||||
public CharChunk getCharChunk() {
|
||||
return charC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value.
|
||||
* Valid only if T_STR or after a conversion was made.
|
||||
* @return the string
|
||||
*/
|
||||
public String getString() {
|
||||
return strValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Charset used for string<->byte conversions.
|
||||
*/
|
||||
public Charset getCharset() {
|
||||
return byteC.getCharset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Charset used for string<->byte conversions.
|
||||
* @param charset The charset
|
||||
*/
|
||||
public void setCharset(Charset charset) {
|
||||
byteC.setCharset(charset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do a char->byte conversion.
|
||||
*/
|
||||
public void toBytes() {
|
||||
if (isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!byteC.isNull()) {
|
||||
type = T_BYTES;
|
||||
return;
|
||||
}
|
||||
toString();
|
||||
type = T_BYTES;
|
||||
Charset charset = byteC.getCharset();
|
||||
ByteBuffer result = charset.encode(strValue);
|
||||
byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to char[] and fill the CharChunk.
|
||||
* XXX Not optimized - it converts to String first.
|
||||
*/
|
||||
public void toChars() {
|
||||
if (isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!charC.isNull()) {
|
||||
type = T_CHARS;
|
||||
return;
|
||||
}
|
||||
// inefficient
|
||||
toString();
|
||||
type = T_CHARS;
|
||||
char cc[] = strValue.toCharArray();
|
||||
charC.setChars(cc, 0, cc.length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the length of the original buffer.
|
||||
* Note that the length in bytes may be different from the length
|
||||
* in chars.
|
||||
* @return the length
|
||||
*/
|
||||
public int getLength() {
|
||||
if(type==T_BYTES) {
|
||||
return byteC.getLength();
|
||||
}
|
||||
if(type==T_CHARS) {
|
||||
return charC.getLength();
|
||||
}
|
||||
if(type==T_STR) {
|
||||
return strValue.length();
|
||||
}
|
||||
toString();
|
||||
if( strValue==null ) {
|
||||
return 0;
|
||||
}
|
||||
return strValue.length();
|
||||
}
|
||||
|
||||
// -------------------- equals --------------------
|
||||
|
||||
/**
|
||||
* Compares the message bytes to the specified String object.
|
||||
* @param s the String to compare
|
||||
* @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean equals(String s) {
|
||||
switch (type) {
|
||||
case T_STR:
|
||||
if (strValue == null) {
|
||||
return s == null;
|
||||
}
|
||||
return strValue.equals( s );
|
||||
case T_CHARS:
|
||||
return charC.equals( s );
|
||||
case T_BYTES:
|
||||
return byteC.equals( s );
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the message bytes to the specified String object.
|
||||
* @param s the String to compare
|
||||
* @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean equalsIgnoreCase(String s) {
|
||||
switch (type) {
|
||||
case T_STR:
|
||||
if (strValue == null) {
|
||||
return s == null;
|
||||
}
|
||||
return strValue.equalsIgnoreCase( s );
|
||||
case T_CHARS:
|
||||
return charC.equalsIgnoreCase( s );
|
||||
case T_BYTES:
|
||||
return byteC.equalsIgnoreCase( s );
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof MessageBytes) {
|
||||
return equals((MessageBytes) obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean equals(MessageBytes mb) {
|
||||
switch (type) {
|
||||
case T_STR:
|
||||
return mb.equals( strValue );
|
||||
}
|
||||
|
||||
if( mb.type != T_CHARS &&
|
||||
mb.type!= T_BYTES ) {
|
||||
// it's a string or int/date string value
|
||||
return equals( mb.toString() );
|
||||
}
|
||||
|
||||
// mb is either CHARS or BYTES.
|
||||
// this is either CHARS or BYTES
|
||||
// Deal with the 4 cases ( in fact 3, one is symmetric)
|
||||
|
||||
if( mb.type == T_CHARS && type==T_CHARS ) {
|
||||
return charC.equals( mb.charC );
|
||||
}
|
||||
if( mb.type==T_BYTES && type== T_BYTES ) {
|
||||
return byteC.equals( mb.byteC );
|
||||
}
|
||||
if( mb.type== T_CHARS && type== T_BYTES ) {
|
||||
return byteC.equals( mb.charC );
|
||||
}
|
||||
if( mb.type== T_BYTES && type== T_CHARS ) {
|
||||
return mb.byteC.equals( charC );
|
||||
}
|
||||
// can't happen
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the message bytes starts with the specified string.
|
||||
* @param s the string
|
||||
* @param pos The start position
|
||||
*/
|
||||
public boolean startsWithIgnoreCase(String s, int pos) {
|
||||
switch (type) {
|
||||
case T_STR:
|
||||
if( strValue==null ) {
|
||||
return false;
|
||||
}
|
||||
if( strValue.length() < pos + s.length() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i=0; i<s.length(); i++ ) {
|
||||
if( Ascii.toLower( s.charAt( i ) ) !=
|
||||
Ascii.toLower( strValue.charAt( pos + i ))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case T_CHARS:
|
||||
return charC.startsWithIgnoreCase( s, pos );
|
||||
case T_BYTES:
|
||||
return byteC.startsWithIgnoreCase( s, pos );
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------- Hash code --------------------
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if( hasHashCode ) {
|
||||
return hashCode;
|
||||
}
|
||||
int code = 0;
|
||||
|
||||
code=hash();
|
||||
hashCode=code;
|
||||
hasHashCode=true;
|
||||
return code;
|
||||
}
|
||||
|
||||
// normal hash.
|
||||
private int hash() {
|
||||
int code=0;
|
||||
switch (type) {
|
||||
case T_STR:
|
||||
// We need to use the same hash function
|
||||
for (int i = 0; i < strValue.length(); i++) {
|
||||
code = code * 37 + strValue.charAt( i );
|
||||
}
|
||||
return code;
|
||||
case T_CHARS:
|
||||
return charC.hash();
|
||||
case T_BYTES:
|
||||
return byteC.hash();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Inefficient initial implementation. Will be replaced on the next
|
||||
// round of tune-up
|
||||
public int indexOf(String s, int starting) {
|
||||
toString();
|
||||
return strValue.indexOf( s, starting );
|
||||
}
|
||||
|
||||
// Inefficient initial implementation. Will be replaced on the next
|
||||
// round of tune-up
|
||||
public int indexOf(String s) {
|
||||
return indexOf( s, 0 );
|
||||
}
|
||||
|
||||
public int indexOfIgnoreCase(String s, int starting) {
|
||||
toString();
|
||||
String upper=strValue.toUpperCase(Locale.ENGLISH);
|
||||
String sU=s.toUpperCase(Locale.ENGLISH);
|
||||
return upper.indexOf( sU, starting );
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the src into this MessageBytes, allocating more space if needed.
|
||||
* @param src The source
|
||||
* @throws IOException Writing overflow data to the output channel failed
|
||||
*/
|
||||
public void duplicate( MessageBytes src ) throws IOException
|
||||
{
|
||||
switch( src.getType() ) {
|
||||
case MessageBytes.T_BYTES:
|
||||
type=T_BYTES;
|
||||
ByteChunk bc=src.getByteChunk();
|
||||
byteC.allocate( 2 * bc.getLength(), -1 );
|
||||
byteC.append( bc );
|
||||
break;
|
||||
case MessageBytes.T_CHARS:
|
||||
type=T_CHARS;
|
||||
CharChunk cc=src.getCharChunk();
|
||||
charC.allocate( 2 * cc.getLength(), -1 );
|
||||
charC.append( cc );
|
||||
break;
|
||||
case MessageBytes.T_STR:
|
||||
type=T_STR;
|
||||
String sc=src.getString();
|
||||
this.setString( sc );
|
||||
break;
|
||||
}
|
||||
setCharset(src.getCharset());
|
||||
}
|
||||
|
||||
// -------------------- Deprecated code --------------------
|
||||
// efficient long
|
||||
// XXX used only for headers - shouldn't be stored here.
|
||||
private long longValue;
|
||||
private boolean hasLongValue=false;
|
||||
|
||||
/**
|
||||
* Set the buffer to the representation of a long.
|
||||
* @param l The long
|
||||
*/
|
||||
public void setLong(long l) {
|
||||
byteC.allocate(32, 64);
|
||||
long current = l;
|
||||
byte[] buf = byteC.getBuffer();
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
if (l == 0) {
|
||||
buf[end++] = (byte) '0';
|
||||
}
|
||||
if (l < 0) {
|
||||
current = -l;
|
||||
buf[end++] = (byte) '-';
|
||||
}
|
||||
while (current > 0) {
|
||||
int digit = (int) (current % 10);
|
||||
current = current / 10;
|
||||
buf[end++] = HexUtils.getHex(digit);
|
||||
}
|
||||
byteC.setOffset(0);
|
||||
byteC.setEnd(end);
|
||||
// Inverting buffer
|
||||
end--;
|
||||
if (l < 0) {
|
||||
start++;
|
||||
}
|
||||
while (end > start) {
|
||||
byte temp = buf[start];
|
||||
buf[start] = buf[end];
|
||||
buf[end] = temp;
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
longValue=l;
|
||||
hasStrValue=false;
|
||||
hasHashCode=false;
|
||||
hasLongValue=true;
|
||||
type=T_BYTES;
|
||||
}
|
||||
|
||||
// Used for headers conversion
|
||||
/**
|
||||
* Convert the buffer to an long, cache the value.
|
||||
* @return the long value
|
||||
*/
|
||||
public long getLong() {
|
||||
if( hasLongValue ) {
|
||||
return longValue;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case T_BYTES:
|
||||
longValue=byteC.getLong();
|
||||
break;
|
||||
default:
|
||||
longValue=Long.parseLong(toString());
|
||||
}
|
||||
|
||||
hasLongValue=true;
|
||||
return longValue;
|
||||
|
||||
}
|
||||
|
||||
// -------------------- Future may be different --------------------
|
||||
|
||||
private static final MessageBytesFactory factory=new MessageBytesFactory();
|
||||
|
||||
private static class MessageBytesFactory {
|
||||
protected MessageBytesFactory() {
|
||||
}
|
||||
public MessageBytes newInstance() {
|
||||
return new MessageBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
711
java/org/apache/tomcat/util/buf/StringCache.java
Normal file
711
java/org/apache/tomcat/util/buf/StringCache.java
Normal file
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* This class implements a String cache for ByteChunk and CharChunk.
|
||||
*
|
||||
* @author Remy Maucherat
|
||||
*/
|
||||
public class StringCache {
|
||||
|
||||
|
||||
private static final Log log = LogFactory.getLog(StringCache.class);
|
||||
|
||||
|
||||
// ------------------------------------------------------- Static Variables
|
||||
|
||||
|
||||
/**
|
||||
* Enabled ?
|
||||
*/
|
||||
protected static boolean byteEnabled = ("true".equals(System.getProperty(
|
||||
"tomcat.util.buf.StringCache.byte.enabled", "false")));
|
||||
|
||||
|
||||
protected static boolean charEnabled = ("true".equals(System.getProperty(
|
||||
"tomcat.util.buf.StringCache.char.enabled", "false")));
|
||||
|
||||
|
||||
protected static int trainThreshold = Integer.parseInt(System.getProperty(
|
||||
"tomcat.util.buf.StringCache.trainThreshold", "20000"));
|
||||
|
||||
|
||||
protected static int cacheSize = Integer.parseInt(System.getProperty(
|
||||
"tomcat.util.buf.StringCache.cacheSize", "200"));
|
||||
|
||||
|
||||
protected static final int maxStringSize =
|
||||
Integer.parseInt(System.getProperty(
|
||||
"tomcat.util.buf.StringCache.maxStringSize", "128"));
|
||||
|
||||
|
||||
/**
|
||||
* Statistics hash map for byte chunk.
|
||||
*/
|
||||
protected static final HashMap<ByteEntry,int[]> bcStats =
|
||||
new HashMap<>(cacheSize);
|
||||
|
||||
|
||||
/**
|
||||
* toString count for byte chunk.
|
||||
*/
|
||||
protected static int bcCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Cache for byte chunk.
|
||||
*/
|
||||
protected static volatile ByteEntry[] bcCache = null;
|
||||
|
||||
|
||||
/**
|
||||
* Statistics hash map for char chunk.
|
||||
*/
|
||||
protected static final HashMap<CharEntry,int[]> ccStats =
|
||||
new HashMap<>(cacheSize);
|
||||
|
||||
|
||||
/**
|
||||
* toString count for char chunk.
|
||||
*/
|
||||
protected static int ccCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Cache for char chunk.
|
||||
*/
|
||||
protected static volatile CharEntry[] ccCache = null;
|
||||
|
||||
|
||||
/**
|
||||
* Access count.
|
||||
*/
|
||||
protected static int accessCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Hit count.
|
||||
*/
|
||||
protected static int hitCount = 0;
|
||||
|
||||
|
||||
// ------------------------------------------------------------ Properties
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the cacheSize.
|
||||
*/
|
||||
public int getCacheSize() {
|
||||
return cacheSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param cacheSize The cacheSize to set.
|
||||
*/
|
||||
public void setCacheSize(int cacheSize) {
|
||||
StringCache.cacheSize = cacheSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the enabled.
|
||||
*/
|
||||
public boolean getByteEnabled() {
|
||||
return byteEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param byteEnabled The enabled to set.
|
||||
*/
|
||||
public void setByteEnabled(boolean byteEnabled) {
|
||||
StringCache.byteEnabled = byteEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the enabled.
|
||||
*/
|
||||
public boolean getCharEnabled() {
|
||||
return charEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param charEnabled The enabled to set.
|
||||
*/
|
||||
public void setCharEnabled(boolean charEnabled) {
|
||||
StringCache.charEnabled = charEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the trainThreshold.
|
||||
*/
|
||||
public int getTrainThreshold() {
|
||||
return trainThreshold;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param trainThreshold The trainThreshold to set.
|
||||
*/
|
||||
public void setTrainThreshold(int trainThreshold) {
|
||||
StringCache.trainThreshold = trainThreshold;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the accessCount.
|
||||
*/
|
||||
public int getAccessCount() {
|
||||
return accessCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the hitCount.
|
||||
*/
|
||||
public int getHitCount() {
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------- Public Static Methods
|
||||
|
||||
|
||||
public void reset() {
|
||||
hitCount = 0;
|
||||
accessCount = 0;
|
||||
synchronized (bcStats) {
|
||||
bcCache = null;
|
||||
bcCount = 0;
|
||||
}
|
||||
synchronized (ccStats) {
|
||||
ccCache = null;
|
||||
ccCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toString(ByteChunk bc) {
|
||||
|
||||
// If the cache is null, then either caching is disabled, or we're
|
||||
// still training
|
||||
if (bcCache == null) {
|
||||
String value = bc.toStringInternal();
|
||||
if (byteEnabled && (value.length() < maxStringSize)) {
|
||||
// If training, everything is synced
|
||||
synchronized (bcStats) {
|
||||
// If the cache has been generated on a previous invocation
|
||||
// while waiting for the lock, just return the toString
|
||||
// value we just calculated
|
||||
if (bcCache != null) {
|
||||
return value;
|
||||
}
|
||||
// Two cases: either we just exceeded the train count, in
|
||||
// which case the cache must be created, or we just update
|
||||
// the count for the string
|
||||
if (bcCount > trainThreshold) {
|
||||
long t1 = System.currentTimeMillis();
|
||||
// Sort the entries according to occurrence
|
||||
TreeMap<Integer,ArrayList<ByteEntry>> tempMap =
|
||||
new TreeMap<>();
|
||||
for (Entry<ByteEntry,int[]> item : bcStats.entrySet()) {
|
||||
ByteEntry entry = item.getKey();
|
||||
int[] countA = item.getValue();
|
||||
Integer count = Integer.valueOf(countA[0]);
|
||||
// Add to the list for that count
|
||||
ArrayList<ByteEntry> list = tempMap.get(count);
|
||||
if (list == null) {
|
||||
// Create list
|
||||
list = new ArrayList<>();
|
||||
tempMap.put(count, list);
|
||||
}
|
||||
list.add(entry);
|
||||
}
|
||||
// Allocate array of the right size
|
||||
int size = bcStats.size();
|
||||
if (size > cacheSize) {
|
||||
size = cacheSize;
|
||||
}
|
||||
ByteEntry[] tempbcCache = new ByteEntry[size];
|
||||
// Fill it up using an alphabetical order
|
||||
// and a dumb insert sort
|
||||
ByteChunk tempChunk = new ByteChunk();
|
||||
int n = 0;
|
||||
while (n < size) {
|
||||
Object key = tempMap.lastKey();
|
||||
ArrayList<ByteEntry> list = tempMap.get(key);
|
||||
for (int i = 0; i < list.size() && n < size; i++) {
|
||||
ByteEntry entry = list.get(i);
|
||||
tempChunk.setBytes(entry.name, 0,
|
||||
entry.name.length);
|
||||
int insertPos = findClosest(tempChunk,
|
||||
tempbcCache, n);
|
||||
if (insertPos == n) {
|
||||
tempbcCache[n + 1] = entry;
|
||||
} else {
|
||||
System.arraycopy(tempbcCache, insertPos + 1,
|
||||
tempbcCache, insertPos + 2,
|
||||
n - insertPos - 1);
|
||||
tempbcCache[insertPos + 1] = entry;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
tempMap.remove(key);
|
||||
}
|
||||
bcCount = 0;
|
||||
bcStats.clear();
|
||||
bcCache = tempbcCache;
|
||||
if (log.isDebugEnabled()) {
|
||||
long t2 = System.currentTimeMillis();
|
||||
log.debug("ByteCache generation time: " +
|
||||
(t2 - t1) + "ms");
|
||||
}
|
||||
} else {
|
||||
bcCount++;
|
||||
// Allocate new ByteEntry for the lookup
|
||||
ByteEntry entry = new ByteEntry();
|
||||
entry.value = value;
|
||||
int[] count = bcStats.get(entry);
|
||||
if (count == null) {
|
||||
int end = bc.getEnd();
|
||||
int start = bc.getStart();
|
||||
// Create byte array and copy bytes
|
||||
entry.name = new byte[bc.getLength()];
|
||||
System.arraycopy(bc.getBuffer(), start, entry.name,
|
||||
0, end - start);
|
||||
// Set encoding
|
||||
entry.charset = bc.getCharset();
|
||||
// Initialize occurrence count to one
|
||||
count = new int[1];
|
||||
count[0] = 1;
|
||||
// Set in the stats hash map
|
||||
bcStats.put(entry, count);
|
||||
} else {
|
||||
count[0] = count[0] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
accessCount++;
|
||||
// Find the corresponding String
|
||||
String result = find(bc);
|
||||
if (result == null) {
|
||||
return bc.toStringInternal();
|
||||
}
|
||||
// Note: We don't care about safety for the stats
|
||||
hitCount++;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static String toString(CharChunk cc) {
|
||||
|
||||
// If the cache is null, then either caching is disabled, or we're
|
||||
// still training
|
||||
if (ccCache == null) {
|
||||
String value = cc.toStringInternal();
|
||||
if (charEnabled && (value.length() < maxStringSize)) {
|
||||
// If training, everything is synced
|
||||
synchronized (ccStats) {
|
||||
// If the cache has been generated on a previous invocation
|
||||
// while waiting for the lock, just return the toString
|
||||
// value we just calculated
|
||||
if (ccCache != null) {
|
||||
return value;
|
||||
}
|
||||
// Two cases: either we just exceeded the train count, in
|
||||
// which case the cache must be created, or we just update
|
||||
// the count for the string
|
||||
if (ccCount > trainThreshold) {
|
||||
long t1 = System.currentTimeMillis();
|
||||
// Sort the entries according to occurrence
|
||||
TreeMap<Integer,ArrayList<CharEntry>> tempMap =
|
||||
new TreeMap<>();
|
||||
for (Entry<CharEntry,int[]> item : ccStats.entrySet()) {
|
||||
CharEntry entry = item.getKey();
|
||||
int[] countA = item.getValue();
|
||||
Integer count = Integer.valueOf(countA[0]);
|
||||
// Add to the list for that count
|
||||
ArrayList<CharEntry> list = tempMap.get(count);
|
||||
if (list == null) {
|
||||
// Create list
|
||||
list = new ArrayList<>();
|
||||
tempMap.put(count, list);
|
||||
}
|
||||
list.add(entry);
|
||||
}
|
||||
// Allocate array of the right size
|
||||
int size = ccStats.size();
|
||||
if (size > cacheSize) {
|
||||
size = cacheSize;
|
||||
}
|
||||
CharEntry[] tempccCache = new CharEntry[size];
|
||||
// Fill it up using an alphabetical order
|
||||
// and a dumb insert sort
|
||||
CharChunk tempChunk = new CharChunk();
|
||||
int n = 0;
|
||||
while (n < size) {
|
||||
Object key = tempMap.lastKey();
|
||||
ArrayList<CharEntry> list = tempMap.get(key);
|
||||
for (int i = 0; i < list.size() && n < size; i++) {
|
||||
CharEntry entry = list.get(i);
|
||||
tempChunk.setChars(entry.name, 0,
|
||||
entry.name.length);
|
||||
int insertPos = findClosest(tempChunk,
|
||||
tempccCache, n);
|
||||
if (insertPos == n) {
|
||||
tempccCache[n + 1] = entry;
|
||||
} else {
|
||||
System.arraycopy(tempccCache, insertPos + 1,
|
||||
tempccCache, insertPos + 2,
|
||||
n - insertPos - 1);
|
||||
tempccCache[insertPos + 1] = entry;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
tempMap.remove(key);
|
||||
}
|
||||
ccCount = 0;
|
||||
ccStats.clear();
|
||||
ccCache = tempccCache;
|
||||
if (log.isDebugEnabled()) {
|
||||
long t2 = System.currentTimeMillis();
|
||||
log.debug("CharCache generation time: " +
|
||||
(t2 - t1) + "ms");
|
||||
}
|
||||
} else {
|
||||
ccCount++;
|
||||
// Allocate new CharEntry for the lookup
|
||||
CharEntry entry = new CharEntry();
|
||||
entry.value = value;
|
||||
int[] count = ccStats.get(entry);
|
||||
if (count == null) {
|
||||
int end = cc.getEnd();
|
||||
int start = cc.getStart();
|
||||
// Create char array and copy chars
|
||||
entry.name = new char[cc.getLength()];
|
||||
System.arraycopy(cc.getBuffer(), start, entry.name,
|
||||
0, end - start);
|
||||
// Initialize occurrence count to one
|
||||
count = new int[1];
|
||||
count[0] = 1;
|
||||
// Set in the stats hash map
|
||||
ccStats.put(entry, count);
|
||||
} else {
|
||||
count[0] = count[0] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
accessCount++;
|
||||
// Find the corresponding String
|
||||
String result = find(cc);
|
||||
if (result == null) {
|
||||
return cc.toStringInternal();
|
||||
}
|
||||
// Note: We don't care about safety for the stats
|
||||
hitCount++;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------- Protected Methods
|
||||
|
||||
|
||||
/**
|
||||
* Compare given byte chunk with byte array.
|
||||
* @param name The name to compare
|
||||
* @param compareTo The compared to data
|
||||
* @return -1, 0 or +1 if inferior, equal, or superior to the String.
|
||||
*/
|
||||
protected static final int compare(ByteChunk name, byte[] compareTo) {
|
||||
int result = 0;
|
||||
|
||||
byte[] b = name.getBuffer();
|
||||
int start = name.getStart();
|
||||
int end = name.getEnd();
|
||||
int len = compareTo.length;
|
||||
|
||||
if ((end - start) < len) {
|
||||
len = end - start;
|
||||
}
|
||||
for (int i = 0; (i < len) && (result == 0); i++) {
|
||||
if (b[i + start] > compareTo[i]) {
|
||||
result = 1;
|
||||
} else if (b[i + start] < compareTo[i]) {
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
if (result == 0) {
|
||||
if (compareTo.length > (end - start)) {
|
||||
result = -1;
|
||||
} else if (compareTo.length < (end - start)) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an entry given its name in the cache and return the associated
|
||||
* String.
|
||||
* @param name The name to find
|
||||
* @return the corresponding value
|
||||
*/
|
||||
protected static final String find(ByteChunk name) {
|
||||
int pos = findClosest(name, bcCache, bcCache.length);
|
||||
if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
|
||||
|| !(name.getCharset().equals(bcCache[pos].charset))) {
|
||||
return null;
|
||||
} else {
|
||||
return bcCache[pos].value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an entry given its name in a sorted array of map elements.
|
||||
* This will return the index for the closest inferior or equal item in the
|
||||
* given array.
|
||||
* @param name The name to find
|
||||
* @param array The array in which to look
|
||||
* @param len The effective length of the array
|
||||
* @return the position of the best match
|
||||
*/
|
||||
protected static final int findClosest(ByteChunk name, ByteEntry[] array,
|
||||
int len) {
|
||||
|
||||
int a = 0;
|
||||
int b = len - 1;
|
||||
|
||||
// Special cases: -1 and 0
|
||||
if (b == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (compare(name, array[0].name) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
i = (b + a) >>> 1;
|
||||
int result = compare(name, array[i].name);
|
||||
if (result == 1) {
|
||||
a = i;
|
||||
} else if (result == 0) {
|
||||
return i;
|
||||
} else {
|
||||
b = i;
|
||||
}
|
||||
if ((b - a) == 1) {
|
||||
int result2 = compare(name, array[b].name);
|
||||
if (result2 < 0) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare given char chunk with char array.
|
||||
* @param name The name to compare
|
||||
* @param compareTo The compared to data
|
||||
* @return -1, 0 or +1 if inferior, equal, or superior to the String.
|
||||
*/
|
||||
protected static final int compare(CharChunk name, char[] compareTo) {
|
||||
int result = 0;
|
||||
|
||||
char[] c = name.getBuffer();
|
||||
int start = name.getStart();
|
||||
int end = name.getEnd();
|
||||
int len = compareTo.length;
|
||||
|
||||
if ((end - start) < len) {
|
||||
len = end - start;
|
||||
}
|
||||
for (int i = 0; (i < len) && (result == 0); i++) {
|
||||
if (c[i + start] > compareTo[i]) {
|
||||
result = 1;
|
||||
} else if (c[i + start] < compareTo[i]) {
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
if (result == 0) {
|
||||
if (compareTo.length > (end - start)) {
|
||||
result = -1;
|
||||
} else if (compareTo.length < (end - start)) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an entry given its name in the cache and return the associated
|
||||
* String.
|
||||
* @param name The name to find
|
||||
* @return the corresponding value
|
||||
*/
|
||||
protected static final String find(CharChunk name) {
|
||||
int pos = findClosest(name, ccCache, ccCache.length);
|
||||
if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) {
|
||||
return null;
|
||||
} else {
|
||||
return ccCache[pos].value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an entry given its name in a sorted array of map elements.
|
||||
* This will return the index for the closest inferior or equal item in the
|
||||
* given array.
|
||||
* @param name The name to find
|
||||
* @param array The array in which to look
|
||||
* @param len The effective length of the array
|
||||
* @return the position of the best match
|
||||
*/
|
||||
protected static final int findClosest(CharChunk name, CharEntry[] array,
|
||||
int len) {
|
||||
|
||||
int a = 0;
|
||||
int b = len - 1;
|
||||
|
||||
// Special cases: -1 and 0
|
||||
if (b == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (compare(name, array[0].name) < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
i = (b + a) >>> 1;
|
||||
int result = compare(name, array[i].name);
|
||||
if (result == 1) {
|
||||
a = i;
|
||||
} else if (result == 0) {
|
||||
return i;
|
||||
} else {
|
||||
b = i;
|
||||
}
|
||||
if ((b - a) == 1) {
|
||||
int result2 = compare(name, array[b].name);
|
||||
if (result2 < 0) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------- ByteEntry Inner Class
|
||||
|
||||
|
||||
private static class ByteEntry {
|
||||
|
||||
private byte[] name = null;
|
||||
private Charset charset = null;
|
||||
private String value = null;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ByteEntry) {
|
||||
return value.equals(((ByteEntry) obj).value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------- CharEntry Inner Class
|
||||
|
||||
|
||||
private static class CharEntry {
|
||||
|
||||
private char[] name = null;
|
||||
private String value = null;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof CharEntry) {
|
||||
return value.equals(((CharEntry) obj).value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
105
java/org/apache/tomcat/util/buf/StringUtils.java
Normal file
105
java/org/apache/tomcat/util/buf/StringUtils.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Utility methods to build a separated list from a given set (not
|
||||
* java.util.Set) of inputs and return that list as a string or append it to an
|
||||
* existing StringBuilder. If the given set is null or empty, an empty string
|
||||
* will be returned.
|
||||
*/
|
||||
public final class StringUtils {
|
||||
|
||||
private static final String EMPTY_STRING = "";
|
||||
|
||||
private StringUtils() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
|
||||
public static String join(String[] array) {
|
||||
if (array == null) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
return join(Arrays.asList(array));
|
||||
}
|
||||
|
||||
|
||||
public static void join(String[] array, char separator, StringBuilder sb) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
join(Arrays.asList(array), separator, sb);
|
||||
}
|
||||
|
||||
|
||||
public static String join(Collection<String> collection) {
|
||||
return join(collection, ',');
|
||||
}
|
||||
|
||||
|
||||
public static String join(Collection<String> collection, char separator) {
|
||||
// Shortcut
|
||||
if (collection == null || collection.isEmpty()) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
join(collection, separator, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
public static void join(Iterable<String> iterable, char separator, StringBuilder sb) {
|
||||
join(iterable, separator,
|
||||
new Function<String>() {@Override public String apply(String t) { return t; }}, sb);
|
||||
}
|
||||
|
||||
|
||||
public static <T> void join(T[] array, char separator, Function<T> function,
|
||||
StringBuilder sb) {
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
join(Arrays.asList(array), separator, function, sb);
|
||||
}
|
||||
|
||||
|
||||
public static <T> void join(Iterable<T> iterable, char separator, Function<T> function,
|
||||
StringBuilder sb) {
|
||||
if (iterable == null) {
|
||||
return;
|
||||
}
|
||||
boolean first = true;
|
||||
for (T value : iterable) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(function.apply(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface Function<T> {
|
||||
public String apply(T t);
|
||||
}
|
||||
}
|
||||
495
java/org/apache/tomcat/util/buf/UDecoder.java
Normal file
495
java/org/apache/tomcat/util/buf/UDecoder.java
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.CharConversionException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* All URL decoding happens here. This way we can reuse, review, optimize
|
||||
* without adding complexity to the buffers.
|
||||
*
|
||||
* The conversion will modify the original buffer.
|
||||
*
|
||||
* @author Costin Manolache
|
||||
*/
|
||||
public final class UDecoder {
|
||||
|
||||
private static final StringManager sm = StringManager.getManager(UDecoder.class);
|
||||
|
||||
private static final Log log = LogFactory.getLog(UDecoder.class);
|
||||
|
||||
public static final boolean ALLOW_ENCODED_SLASH =
|
||||
Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false"));
|
||||
|
||||
private static class DecodeException extends CharConversionException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public DecodeException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
// This class does not provide a stack trace
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Unexpected end of data. */
|
||||
private static final IOException EXCEPTION_EOF = new DecodeException(sm.getString("uDecoder.eof"));
|
||||
|
||||
/** %xx with not-hex digit */
|
||||
private static final IOException EXCEPTION_NOT_HEX_DIGIT = new DecodeException(
|
||||
"isHexDigit");
|
||||
|
||||
/** %-encoded slash is forbidden in resource path */
|
||||
private static final IOException EXCEPTION_SLASH = new DecodeException(
|
||||
"noSlash");
|
||||
|
||||
public UDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* URLDecode, will modify the source.
|
||||
* @param mb The URL encoded bytes
|
||||
* @param query <code>true</code> if this is a query string
|
||||
* @throws IOException Invalid %xx URL encoding
|
||||
*/
|
||||
public void convert( ByteChunk mb, boolean query )
|
||||
throws IOException
|
||||
{
|
||||
int start=mb.getOffset();
|
||||
byte buff[]=mb.getBytes();
|
||||
int end=mb.getEnd();
|
||||
|
||||
int idx= ByteChunk.findByte( buff, start, end, (byte) '%' );
|
||||
int idx2=-1;
|
||||
if( query ) {
|
||||
idx2= ByteChunk.findByte( buff, start, (idx >= 0 ? idx : end), (byte) '+' );
|
||||
}
|
||||
if( idx<0 && idx2<0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// idx will be the smallest positive index ( first % or + )
|
||||
if( (idx2 >= 0 && idx2 < idx) || idx < 0 ) {
|
||||
idx=idx2;
|
||||
}
|
||||
|
||||
final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
|
||||
|
||||
for( int j=idx; j<end; j++, idx++ ) {
|
||||
if( buff[ j ] == '+' && query) {
|
||||
buff[idx]= (byte)' ' ;
|
||||
} else if( buff[ j ] != '%' ) {
|
||||
buff[idx]= buff[j];
|
||||
} else {
|
||||
// read next 2 digits
|
||||
if( j+2 >= end ) {
|
||||
throw EXCEPTION_EOF;
|
||||
}
|
||||
byte b1= buff[j+1];
|
||||
byte b2=buff[j+2];
|
||||
if( !isHexDigit( b1 ) || ! isHexDigit(b2 )) {
|
||||
throw EXCEPTION_NOT_HEX_DIGIT;
|
||||
}
|
||||
|
||||
j+=2;
|
||||
int res=x2c( b1, b2 );
|
||||
if (noSlash && (res == '/')) {
|
||||
throw EXCEPTION_SLASH;
|
||||
}
|
||||
buff[idx]=(byte)res;
|
||||
}
|
||||
}
|
||||
|
||||
mb.setEnd( idx );
|
||||
|
||||
}
|
||||
|
||||
// -------------------- Additional methods --------------------
|
||||
// XXX What do we do about charset ????
|
||||
|
||||
/**
|
||||
* In-buffer processing - the buffer will be modified.
|
||||
* @param mb The URL encoded chars
|
||||
* @param query <code>true</code> if this is a query string
|
||||
* @throws IOException Invalid %xx URL encoding
|
||||
*/
|
||||
public void convert( CharChunk mb, boolean query )
|
||||
throws IOException
|
||||
{
|
||||
// log( "Converting a char chunk ");
|
||||
int start=mb.getOffset();
|
||||
char buff[]=mb.getBuffer();
|
||||
int cend=mb.getEnd();
|
||||
|
||||
int idx= CharChunk.indexOf( buff, start, cend, '%' );
|
||||
int idx2=-1;
|
||||
if( query ) {
|
||||
idx2= CharChunk.indexOf( buff, start, (idx >= 0 ? idx : cend), '+' );
|
||||
}
|
||||
if( idx<0 && idx2<0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// idx will be the smallest positive index ( first % or + )
|
||||
if( (idx2 >= 0 && idx2 < idx) || idx < 0 ) {
|
||||
idx=idx2;
|
||||
}
|
||||
|
||||
final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
|
||||
|
||||
for( int j=idx; j<cend; j++, idx++ ) {
|
||||
if( buff[ j ] == '+' && query ) {
|
||||
buff[idx]=( ' ' );
|
||||
} else if( buff[ j ] != '%' ) {
|
||||
buff[idx]=buff[j];
|
||||
} else {
|
||||
// read next 2 digits
|
||||
if( j+2 >= cend ) {
|
||||
// invalid
|
||||
throw EXCEPTION_EOF;
|
||||
}
|
||||
char b1= buff[j+1];
|
||||
char b2=buff[j+2];
|
||||
if( !isHexDigit( b1 ) || ! isHexDigit(b2 )) {
|
||||
throw EXCEPTION_NOT_HEX_DIGIT;
|
||||
}
|
||||
|
||||
j+=2;
|
||||
int res=x2c( b1, b2 );
|
||||
if (noSlash && (res == '/')) {
|
||||
throw EXCEPTION_SLASH;
|
||||
}
|
||||
buff[idx]=(char)res;
|
||||
}
|
||||
}
|
||||
mb.setEnd( idx );
|
||||
}
|
||||
|
||||
/**
|
||||
* URLDecode, will modify the source
|
||||
* @param mb The URL encoded String, bytes or chars
|
||||
* @param query <code>true</code> if this is a query string
|
||||
* @throws IOException Invalid %xx URL encoding
|
||||
*/
|
||||
public void convert(MessageBytes mb, boolean query)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
switch (mb.getType()) {
|
||||
case MessageBytes.T_STR:
|
||||
String strValue=mb.toString();
|
||||
if( strValue==null ) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mb.setString( convert( strValue, query ));
|
||||
} catch (RuntimeException ex) {
|
||||
throw new DecodeException(ex.getMessage());
|
||||
}
|
||||
break;
|
||||
case MessageBytes.T_CHARS:
|
||||
CharChunk charC=mb.getCharChunk();
|
||||
convert( charC, query );
|
||||
break;
|
||||
case MessageBytes.T_BYTES:
|
||||
ByteChunk bytesC=mb.getByteChunk();
|
||||
convert( bytesC, query );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* %xx decoding of a string. FIXME: this is inefficient.
|
||||
* @param str The URL encoded string
|
||||
* @param query <code>true</code> if this is a query string
|
||||
* @return the decoded string
|
||||
*/
|
||||
public final String convert(String str, boolean query)
|
||||
{
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if( (!query || str.indexOf( '+' ) < 0) && str.indexOf( '%' ) < 0 ) {
|
||||
return str;
|
||||
}
|
||||
|
||||
final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
|
||||
|
||||
StringBuilder dec = new StringBuilder(); // decoded string output
|
||||
int strPos = 0;
|
||||
int strLen = str.length();
|
||||
|
||||
dec.ensureCapacity(str.length());
|
||||
while (strPos < strLen) {
|
||||
int laPos; // lookahead position
|
||||
|
||||
// look ahead to next URLencoded metacharacter, if any
|
||||
for (laPos = strPos; laPos < strLen; laPos++) {
|
||||
char laChar = str.charAt(laPos);
|
||||
if ((laChar == '+' && query) || (laChar == '%')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if there were non-metacharacters, copy them all as a block
|
||||
if (laPos > strPos) {
|
||||
dec.append(str.substring(strPos,laPos));
|
||||
strPos = laPos;
|
||||
}
|
||||
|
||||
// shortcut out of here if we're at the end of the string
|
||||
if (strPos >= strLen) {
|
||||
break;
|
||||
}
|
||||
|
||||
// process next metacharacter
|
||||
char metaChar = str.charAt(strPos);
|
||||
if (metaChar == '+') {
|
||||
dec.append(' ');
|
||||
strPos++;
|
||||
continue;
|
||||
} else if (metaChar == '%') {
|
||||
// We throw the original exception - the super will deal with
|
||||
// it
|
||||
// try {
|
||||
char res = (char) Integer.parseInt(
|
||||
str.substring(strPos + 1, strPos + 3), 16);
|
||||
if (noSlash && (res == '/')) {
|
||||
throw new IllegalArgumentException(sm.getString("uDecoder.noSlash"));
|
||||
}
|
||||
dec.append(res);
|
||||
strPos += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return dec.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded String.
|
||||
* When the byte array is converted to a string, ISO-885901 is used. This
|
||||
* may be different than some other servers. It is assumed the string is not
|
||||
* a query string.
|
||||
*
|
||||
* @param str The url-encoded string
|
||||
* @return the decoded string
|
||||
* @exception IllegalArgumentException if a '%' character is not followed
|
||||
* by a valid 2-digit hexadecimal number
|
||||
*/
|
||||
public static String URLDecode(String str) {
|
||||
return URLDecode(str, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded String. It is assumed the
|
||||
* string is not a query string.
|
||||
*
|
||||
* @param str The url-encoded string
|
||||
* @param enc The encoding to use; if null, ISO-885901 is used. If
|
||||
* an unsupported encoding is specified null will be returned
|
||||
* @return the decoded string
|
||||
* @exception IllegalArgumentException if a '%' character is not followed
|
||||
* by a valid 2-digit hexadecimal number
|
||||
*
|
||||
* @deprecated This method will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public static String URLDecode(String str, String enc) {
|
||||
return URLDecode(str, enc, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded String. It is assumed the
|
||||
* string is not a query string.
|
||||
*
|
||||
* @param str The url-encoded string
|
||||
* @param charset The character encoding to use; if null, ISO-8859-1 is
|
||||
* used.
|
||||
* @return the decoded string
|
||||
* @exception IllegalArgumentException if a '%' character is not followed
|
||||
* by a valid 2-digit hexadecimal number
|
||||
*/
|
||||
public static String URLDecode(String str, Charset charset) {
|
||||
return URLDecode(str, charset, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded String.
|
||||
*
|
||||
* @param str The url-encoded string
|
||||
* @param enc The encoding to use; if null, ISO-8859-1 is used. If
|
||||
* an unsupported encoding is specified null will be returned
|
||||
* @param isQuery Is this a query string being processed
|
||||
* @return the decoded string
|
||||
* @exception IllegalArgumentException if a '%' character is not followed
|
||||
* by a valid 2-digit hexadecimal number
|
||||
*
|
||||
* @deprecated This method will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public static String URLDecode(String str, String enc, boolean isQuery) {
|
||||
Charset charset = null;
|
||||
|
||||
if (enc != null) {
|
||||
try {
|
||||
charset = B2CConverter.getCharset(enc);
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("uDecoder.urlDecode.uee", enc), uee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return URLDecode(str, charset, isQuery);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode and return the specified URL-encoded byte array.
|
||||
*
|
||||
* @param bytes The url-encoded byte array
|
||||
* @param enc The encoding to use; if null, ISO-8859-1 is used. If
|
||||
* an unsupported encoding is specified null will be returned
|
||||
* @param isQuery Is this a query string being processed
|
||||
* @return the decoded string
|
||||
* @exception IllegalArgumentException if a '%' character is not followed
|
||||
* by a valid 2-digit hexadecimal number
|
||||
*
|
||||
* @deprecated This method will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public static String URLDecode(byte[] bytes, String enc, boolean isQuery) {
|
||||
throw new IllegalArgumentException(sm.getString("udecoder.urlDecode.iae"));
|
||||
}
|
||||
|
||||
|
||||
private static String URLDecode(String str, Charset charset, boolean isQuery) {
|
||||
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str.indexOf('%') == -1) {
|
||||
// No %nn sequences, so return string unchanged
|
||||
return str;
|
||||
}
|
||||
|
||||
if (charset == null) {
|
||||
charset = StandardCharsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decoding is required.
|
||||
*
|
||||
* Potential complications:
|
||||
* - The source String may be partially decoded so it is not valid to
|
||||
* assume that the source String is ASCII.
|
||||
* - Have to process as characters since there is no guarantee that the
|
||||
* byte sequence for '%' is going to be the same in all character
|
||||
* sets.
|
||||
* - We don't know how many '%nn' sequences are required for a single
|
||||
* character. It varies between character sets and some use a variable
|
||||
* length.
|
||||
*/
|
||||
|
||||
// This isn't perfect but it is a reasonable guess for the size of the
|
||||
// array required
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(str.length() * 2);
|
||||
|
||||
OutputStreamWriter osw = new OutputStreamWriter(baos, charset);
|
||||
|
||||
char[] sourceChars = str.toCharArray();
|
||||
int len = sourceChars.length;
|
||||
int ix = 0;
|
||||
|
||||
try {
|
||||
while (ix < len) {
|
||||
char c = sourceChars[ix++];
|
||||
if (c == '%') {
|
||||
osw.flush();
|
||||
if (ix + 2 > len) {
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("uDecoder.urlDecode.missingDigit", str));
|
||||
}
|
||||
char c1 = sourceChars[ix++];
|
||||
char c2 = sourceChars[ix++];
|
||||
if (isHexDigit(c1) && isHexDigit(c2)) {
|
||||
baos.write(x2c(c1, c2));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("uDecoder.urlDecode.missingDigit", str));
|
||||
}
|
||||
} else if (c == '+' && isQuery) {
|
||||
osw.append(' ');
|
||||
} else {
|
||||
osw.append(c);
|
||||
}
|
||||
}
|
||||
osw.flush();
|
||||
|
||||
return baos.toString(charset.name());
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("uDecoder.urlDecode.conversionError", str, charset.name()), ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean isHexDigit( int c ) {
|
||||
return ( ( c>='0' && c<='9' ) ||
|
||||
( c>='a' && c<='f' ) ||
|
||||
( c>='A' && c<='F' ));
|
||||
}
|
||||
|
||||
|
||||
private static int x2c( byte b1, byte b2 ) {
|
||||
int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
|
||||
(b1 -'0');
|
||||
digit*=16;
|
||||
digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
|
||||
(b2 -'0');
|
||||
return digit;
|
||||
}
|
||||
|
||||
|
||||
private static int x2c( char b1, char b2 ) {
|
||||
int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
|
||||
(b1 -'0');
|
||||
digit*=16;
|
||||
digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
|
||||
(b2 -'0');
|
||||
return digit;
|
||||
}
|
||||
}
|
||||
167
java/org/apache/tomcat/util/buf/UEncoder.java
Normal file
167
java/org/apache/tomcat/util/buf/UEncoder.java
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* Efficient implementation of an UTF-8 encoder.
|
||||
* This class is not thread safe - you need one encoder per thread.
|
||||
* The encoder will save and recycle the internal objects, avoiding
|
||||
* garbage.
|
||||
*
|
||||
* You can add extra characters that you want preserved, for example
|
||||
* while encoding a URL you can add "/".
|
||||
*
|
||||
* @author Costin Manolache
|
||||
*/
|
||||
public final class UEncoder {
|
||||
|
||||
public enum SafeCharsSet {
|
||||
WITH_SLASH("/"), DEFAULT("");
|
||||
private final BitSet safeChars;
|
||||
|
||||
private BitSet getSafeChars() {
|
||||
return this.safeChars;
|
||||
}
|
||||
|
||||
private SafeCharsSet(String additionalSafeChars) {
|
||||
safeChars = initialSafeChars();
|
||||
for (char c : additionalSafeChars.toCharArray()) {
|
||||
safeChars.set(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not static - the set may differ ( it's better than adding
|
||||
// an extra check for "/", "+", etc
|
||||
private BitSet safeChars=null;
|
||||
private C2BConverter c2b=null;
|
||||
private ByteChunk bb=null;
|
||||
private CharChunk cb=null;
|
||||
private CharChunk output=null;
|
||||
|
||||
/**
|
||||
* Create a UEncoder with an unmodifiable safe character set.
|
||||
*
|
||||
* @param safeCharsSet safe characters for this encoder
|
||||
*/
|
||||
public UEncoder(SafeCharsSet safeCharsSet) {
|
||||
this.safeChars = safeCharsSet.getSafeChars();
|
||||
}
|
||||
|
||||
/**
|
||||
* URL Encode string, using a specified encoding.
|
||||
*
|
||||
* @param s string to be encoded
|
||||
* @param start the beginning index, inclusive
|
||||
* @param end the ending index, exclusive
|
||||
*
|
||||
* @return A new CharChunk contained the URL encoded string
|
||||
*
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public CharChunk encodeURL(String s, int start, int end)
|
||||
throws IOException {
|
||||
if (c2b == null) {
|
||||
bb = new ByteChunk(8); // small enough.
|
||||
cb = new CharChunk(2); // small enough.
|
||||
output = new CharChunk(64); // small enough.
|
||||
c2b = new C2BConverter(StandardCharsets.UTF_8);
|
||||
} else {
|
||||
bb.recycle();
|
||||
cb.recycle();
|
||||
output.recycle();
|
||||
}
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (safeChars.get(c)) {
|
||||
output.append(c);
|
||||
} else {
|
||||
cb.append(c);
|
||||
c2b.convert(cb, bb);
|
||||
|
||||
// "surrogate" - UTF is _not_ 16 bit, but 21 !!!!
|
||||
// ( while UCS is 31 ). Amazing...
|
||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
||||
if ((i+1) < end) {
|
||||
char d = s.charAt(i+1);
|
||||
if (d >= 0xDC00 && d <= 0xDFFF) {
|
||||
cb.append(d);
|
||||
c2b.convert(cb, bb);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
urlEncode(output, bb);
|
||||
cb.recycle();
|
||||
bb.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
protected void urlEncode(CharChunk out, ByteChunk bb)
|
||||
throws IOException {
|
||||
byte[] bytes = bb.getBuffer();
|
||||
for (int j = bb.getStart(); j < bb.getEnd(); j++) {
|
||||
out.append('%');
|
||||
char ch = Character.forDigit((bytes[j] >> 4) & 0xF, 16);
|
||||
out.append(ch);
|
||||
ch = Character.forDigit(bytes[j] & 0xF, 16);
|
||||
out.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- Internal implementation --------------------
|
||||
|
||||
private static BitSet initialSafeChars() {
|
||||
BitSet initialSafeChars=new BitSet(128);
|
||||
int i;
|
||||
for (i = 'a'; i <= 'z'; i++) {
|
||||
initialSafeChars.set(i);
|
||||
}
|
||||
for (i = 'A'; i <= 'Z'; i++) {
|
||||
initialSafeChars.set(i);
|
||||
}
|
||||
for (i = '0'; i <= '9'; i++) {
|
||||
initialSafeChars.set(i);
|
||||
}
|
||||
//safe
|
||||
initialSafeChars.set('$');
|
||||
initialSafeChars.set('-');
|
||||
initialSafeChars.set('_');
|
||||
initialSafeChars.set('.');
|
||||
|
||||
// Dangerous: someone may treat this as " "
|
||||
// RFC1738 does allow it, it's not reserved
|
||||
// initialSafeChars.set('+');
|
||||
//extra
|
||||
initialSafeChars.set('!');
|
||||
initialSafeChars.set('*');
|
||||
initialSafeChars.set('\'');
|
||||
initialSafeChars.set('(');
|
||||
initialSafeChars.set(')');
|
||||
initialSafeChars.set(',');
|
||||
return initialSafeChars;
|
||||
}
|
||||
}
|
||||
197
java/org/apache/tomcat/util/buf/UriUtil.java
Normal file
197
java/org/apache/tomcat/util/buf/UriUtil.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility class for working with URIs and URLs.
|
||||
*/
|
||||
public final class UriUtil {
|
||||
|
||||
private static final char[] HEX =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
private static final Pattern PATTERN_EXCLAMATION_MARK = Pattern.compile("!/");
|
||||
private static final Pattern PATTERN_CARET = Pattern.compile("\\^/");
|
||||
private static final Pattern PATTERN_ASTERISK = Pattern.compile("\\*/");
|
||||
private static final Pattern PATTERN_CUSTOM;
|
||||
private static final String REPLACE_CUSTOM;
|
||||
|
||||
private static final String WAR_SEPARATOR;
|
||||
|
||||
static {
|
||||
String custom = System.getProperty("org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR");
|
||||
if (custom == null) {
|
||||
WAR_SEPARATOR = "*/";
|
||||
PATTERN_CUSTOM = null;
|
||||
REPLACE_CUSTOM = null;
|
||||
} else {
|
||||
WAR_SEPARATOR = custom + "/";
|
||||
PATTERN_CUSTOM = Pattern.compile(Pattern.quote(WAR_SEPARATOR));
|
||||
StringBuffer sb = new StringBuffer(custom.length() * 3);
|
||||
// Deliberately use the platform's default encoding
|
||||
byte[] ba = custom.getBytes();
|
||||
for (int j = 0; j < ba.length; j++) {
|
||||
// Converting each byte in the buffer
|
||||
byte toEncode = ba[j];
|
||||
sb.append('%');
|
||||
int low = toEncode & 0x0f;
|
||||
int high = (toEncode & 0xf0) >> 4;
|
||||
sb.append(HEX[high]);
|
||||
sb.append(HEX[low]);
|
||||
}
|
||||
REPLACE_CUSTOM = sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private UriUtil() {
|
||||
// Utility class. Hide default constructor
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the character is allowed in the scheme of a URI.
|
||||
* See RFC 2396, Section 3.1
|
||||
*
|
||||
* @param c The character to test
|
||||
*
|
||||
* @return {@code true} if a the character is allowed, otherwise {code
|
||||
* @false}
|
||||
*/
|
||||
private static boolean isSchemeChar(char c) {
|
||||
return Character.isLetterOrDigit(c) || c == '+' || c == '-' || c == '.';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a URI string has a <code>scheme</code> component.
|
||||
*
|
||||
* @param uri The URI to test
|
||||
*
|
||||
* @return {@code true} if a scheme is present, otherwise {code @false}
|
||||
*/
|
||||
public static boolean hasScheme(CharSequence uri) {
|
||||
int len = uri.length();
|
||||
for(int i=0; i < len ; i++) {
|
||||
char c = uri.charAt(i);
|
||||
if(c == ':') {
|
||||
return i > 0;
|
||||
} else if(!UriUtil.isSchemeChar(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static URL buildJarUrl(File jarFile) throws MalformedURLException {
|
||||
return buildJarUrl(jarFile, null);
|
||||
}
|
||||
|
||||
|
||||
public static URL buildJarUrl(File jarFile, String entryPath) throws MalformedURLException {
|
||||
return buildJarUrl(jarFile.toURI().toString(), entryPath);
|
||||
}
|
||||
|
||||
|
||||
public static URL buildJarUrl(String fileUrlString) throws MalformedURLException {
|
||||
return buildJarUrl(fileUrlString, null);
|
||||
}
|
||||
|
||||
|
||||
public static URL buildJarUrl(String fileUrlString, String entryPath) throws MalformedURLException {
|
||||
String safeString = makeSafeForJarUrl(fileUrlString);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(safeString);
|
||||
sb.append("!/");
|
||||
if (entryPath != null) {
|
||||
sb.append(makeSafeForJarUrl(entryPath));
|
||||
}
|
||||
return new URL("jar", null, -1, sb.toString());
|
||||
}
|
||||
|
||||
|
||||
public static URL buildJarSafeUrl(File file) throws MalformedURLException {
|
||||
String safe = makeSafeForJarUrl(file.toURI().toString());
|
||||
return new URL(safe);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When testing on markt's desktop each iteration was taking ~1420ns when
|
||||
* using String.replaceAll().
|
||||
*
|
||||
* Switching the implementation to use pre-compiled patterns and
|
||||
* Pattern.matcher(input).replaceAll(replacement) reduced this by ~10%.
|
||||
*
|
||||
* Note: Given the very small absolute time of a single iteration, even for
|
||||
* a web application with 1000 JARs this is only going to add ~3ms.
|
||||
* It is therefore unlikely that further optimisation will be
|
||||
* necessary.
|
||||
*/
|
||||
/*
|
||||
* Pulled out into a separate method in case we need to handle other unusual
|
||||
* sequences in the future.
|
||||
*/
|
||||
private static String makeSafeForJarUrl(String input) {
|
||||
// Since "!/" has a special meaning in a JAR URL, make sure that the
|
||||
// sequence is properly escaped if present.
|
||||
String tmp = PATTERN_EXCLAMATION_MARK.matcher(input).replaceAll("%21/");
|
||||
// Tomcat's custom jar:war: URL handling treats */ and ^/ as special
|
||||
tmp = PATTERN_CARET.matcher(tmp).replaceAll("%5e/");
|
||||
tmp = PATTERN_ASTERISK.matcher(tmp).replaceAll("%2a/");
|
||||
if (PATTERN_CUSTOM != null) {
|
||||
tmp = PATTERN_CUSTOM.matcher(tmp).replaceAll(REPLACE_CUSTOM);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a URL of the form <code>war:file:...</code> to
|
||||
* <code>jar:file:...</code>.
|
||||
*
|
||||
* @param warUrl The WAR URL to convert
|
||||
*
|
||||
* @return The equivalent JAR URL
|
||||
*
|
||||
* @throws MalformedURLException If the conversion fails
|
||||
*/
|
||||
public static URL warToJar(URL warUrl) throws MalformedURLException {
|
||||
// Assumes that the spec is absolute and starts war:file:/...
|
||||
String file = warUrl.getFile();
|
||||
if (file.contains("*/")) {
|
||||
file = file.replaceFirst("\\*/", "!/");
|
||||
} else if (file.contains("^/")) {
|
||||
file = file.replaceFirst("\\^/", "!/");
|
||||
} else if (PATTERN_CUSTOM != null) {
|
||||
file = file.replaceFirst(PATTERN_CUSTOM.pattern(), "!/");
|
||||
}
|
||||
|
||||
return new URL("jar", warUrl.getHost(), warUrl.getPort(), file);
|
||||
}
|
||||
|
||||
|
||||
public static String getWarSeparator() {
|
||||
return WAR_SEPARATOR;
|
||||
}
|
||||
}
|
||||
299
java/org/apache/tomcat/util/buf/Utf8Decoder.java
Normal file
299
java/org/apache/tomcat/util/buf/Utf8Decoder.java
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Decodes bytes to UTF-8. Extracted from Apache Harmony and modified to reject
|
||||
* code points from U+D800 to U+DFFF as per RFC3629. The standard Java decoder
|
||||
* does not reject these. It has also been modified to reject code points
|
||||
* greater than U+10FFFF which the standard Java decoder rejects but the harmony
|
||||
* one does not.
|
||||
*/
|
||||
public class Utf8Decoder extends CharsetDecoder {
|
||||
|
||||
// The next table contains information about UTF-8 charset and
|
||||
// correspondence of 1st byte to the length of sequence
|
||||
// For information please visit http://www.ietf.org/rfc/rfc3629.txt
|
||||
//
|
||||
// Please note, o means 0, actually.
|
||||
// -------------------------------------------------------------------
|
||||
// 0 1 2 3 Value
|
||||
// -------------------------------------------------------------------
|
||||
// oxxxxxxx 00000000 00000000 0xxxxxxx
|
||||
// 11oyyyyy 1oxxxxxx 00000000 00000yyy yyxxxxxx
|
||||
// 111ozzzz 1oyyyyyy 1oxxxxxx 00000000 zzzzyyyy yyxxxxxx
|
||||
// 1111ouuu 1ouuzzzz 1oyyyyyy 1oxxxxxx 000uuuuu zzzzyyyy yyxxxxxx
|
||||
private static final int remainingBytes[] = {
|
||||
// 1owwwwww
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
// 11oyyyyy
|
||||
-1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
// 111ozzzz
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
// 1111ouuu
|
||||
3, 3, 3, 3, 3, -1, -1, -1,
|
||||
// > 11110111
|
||||
-1, -1, -1, -1, -1, -1, -1, -1};
|
||||
private static final int remainingNumbers[] = {0, // 0 1 2 3
|
||||
4224, // (01o00000b << 6)+(1o000000b)
|
||||
401536, // (011o0000b << 12)+(1o000000b << 6)+(1o000000b)
|
||||
29892736 // (0111o000b << 18)+(1o000000b << 12)+(1o000000b <<
|
||||
// 6)+(1o000000b)
|
||||
};
|
||||
private static final int lowerEncodingLimit[] = {-1, 0x80, 0x800, 0x10000};
|
||||
|
||||
|
||||
public Utf8Decoder() {
|
||||
super(StandardCharsets.UTF_8, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
|
||||
if (in.hasArray() && out.hasArray()) {
|
||||
return decodeHasArray(in, out);
|
||||
}
|
||||
return decodeNotHasArray(in, out);
|
||||
}
|
||||
|
||||
|
||||
private CoderResult decodeNotHasArray(ByteBuffer in, CharBuffer out) {
|
||||
int outRemaining = out.remaining();
|
||||
int pos = in.position();
|
||||
int limit = in.limit();
|
||||
try {
|
||||
while (pos < limit) {
|
||||
if (outRemaining == 0) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
int jchar = in.get();
|
||||
if (jchar < 0) {
|
||||
jchar = jchar & 0x7F;
|
||||
int tail = remainingBytes[jchar];
|
||||
if (tail == -1) {
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
if (limit - pos < 1 + tail) {
|
||||
// No early test for invalid sequences here as peeking
|
||||
// at the next byte is harder
|
||||
return CoderResult.UNDERFLOW;
|
||||
}
|
||||
int nextByte;
|
||||
for (int i = 0; i < tail; i++) {
|
||||
nextByte = in.get() & 0xFF;
|
||||
if ((nextByte & 0xC0) != 0x80) {
|
||||
return CoderResult.malformedForLength(1 + i);
|
||||
}
|
||||
jchar = (jchar << 6) + nextByte;
|
||||
}
|
||||
jchar -= remainingNumbers[tail];
|
||||
if (jchar < lowerEncodingLimit[tail]) {
|
||||
// Should have been encoded in a fewer octets
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
pos += tail;
|
||||
}
|
||||
// Apache Tomcat added test
|
||||
if (jchar >= 0xD800 && jchar <= 0xDFFF) {
|
||||
return CoderResult.unmappableForLength(3);
|
||||
}
|
||||
// Apache Tomcat added test
|
||||
if (jchar > 0x10FFFF) {
|
||||
return CoderResult.unmappableForLength(4);
|
||||
}
|
||||
if (jchar <= 0xffff) {
|
||||
out.put((char) jchar);
|
||||
outRemaining--;
|
||||
} else {
|
||||
if (outRemaining < 2) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
out.put((char) ((jchar >> 0xA) + 0xD7C0));
|
||||
out.put((char) ((jchar & 0x3FF) + 0xDC00));
|
||||
outRemaining -= 2;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return CoderResult.UNDERFLOW;
|
||||
} finally {
|
||||
in.position(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private CoderResult decodeHasArray(ByteBuffer in, CharBuffer out) {
|
||||
int outRemaining = out.remaining();
|
||||
int pos = in.position();
|
||||
int limit = in.limit();
|
||||
final byte[] bArr = in.array();
|
||||
final char[] cArr = out.array();
|
||||
final int inIndexLimit = limit + in.arrayOffset();
|
||||
int inIndex = pos + in.arrayOffset();
|
||||
int outIndex = out.position() + out.arrayOffset();
|
||||
// if someone would change the limit in process,
|
||||
// he would face consequences
|
||||
for (; inIndex < inIndexLimit && outRemaining > 0; inIndex++) {
|
||||
int jchar = bArr[inIndex];
|
||||
if (jchar < 0) {
|
||||
jchar = jchar & 0x7F;
|
||||
// If first byte is invalid, tail will be set to -1
|
||||
int tail = remainingBytes[jchar];
|
||||
if (tail == -1) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// Additional checks to detect invalid sequences ASAP
|
||||
// Checks derived from Unicode 6.2, Chapter 3, Table 3-7
|
||||
// Check 2nd byte
|
||||
int tailAvailable = inIndexLimit - inIndex - 1;
|
||||
if (tailAvailable > 0) {
|
||||
// First byte C2..DF, second byte 80..BF
|
||||
if (jchar > 0x41 && jchar < 0x60 &&
|
||||
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte E0, second byte A0..BF
|
||||
if (jchar == 0x60 && (bArr[inIndex + 1] & 0xE0) != 0xA0) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte E1..EC, second byte 80..BF
|
||||
if (jchar > 0x60 && jchar < 0x6D &&
|
||||
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte ED, second byte 80..9F
|
||||
if (jchar == 0x6D && (bArr[inIndex + 1] & 0xE0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte EE..EF, second byte 80..BF
|
||||
if (jchar > 0x6D && jchar < 0x70 &&
|
||||
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte F0, second byte 90..BF
|
||||
if (jchar == 0x70 &&
|
||||
((bArr[inIndex + 1] & 0xFF) < 0x90 ||
|
||||
(bArr[inIndex + 1] & 0xFF) > 0xBF)) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte F1..F3, second byte 80..BF
|
||||
if (jchar > 0x70 && jchar < 0x74 &&
|
||||
(bArr[inIndex + 1] & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
// First byte F4, second byte 80..8F
|
||||
if (jchar == 0x74 &&
|
||||
(bArr[inIndex + 1] & 0xF0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
}
|
||||
// Check third byte if present and expected
|
||||
if (tailAvailable > 1 && tail > 1) {
|
||||
if ((bArr[inIndex + 2] & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(2);
|
||||
}
|
||||
}
|
||||
// Check fourth byte if present and expected
|
||||
if (tailAvailable > 2 && tail > 2) {
|
||||
if ((bArr[inIndex + 3] & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(3);
|
||||
}
|
||||
}
|
||||
if (tailAvailable < tail) {
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < tail; i++) {
|
||||
int nextByte = bArr[inIndex + i + 1] & 0xFF;
|
||||
if ((nextByte & 0xC0) != 0x80) {
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1 + i);
|
||||
}
|
||||
jchar = (jchar << 6) + nextByte;
|
||||
}
|
||||
jchar -= remainingNumbers[tail];
|
||||
if (jchar < lowerEncodingLimit[tail]) {
|
||||
// Should have been encoded in fewer octets
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
inIndex += tail;
|
||||
}
|
||||
// Apache Tomcat added test
|
||||
if (jchar >= 0xD800 && jchar <= 0xDFFF) {
|
||||
return CoderResult.unmappableForLength(3);
|
||||
}
|
||||
// Apache Tomcat added test
|
||||
if (jchar > 0x10FFFF) {
|
||||
return CoderResult.unmappableForLength(4);
|
||||
}
|
||||
if (jchar <= 0xffff) {
|
||||
cArr[outIndex++] = (char) jchar;
|
||||
outRemaining--;
|
||||
} else {
|
||||
if (outRemaining < 2) {
|
||||
// Encoded with 4 bytes. inIndex currently points
|
||||
// to the final byte. Move it back to first byte.
|
||||
inIndex -= 3;
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
cArr[outIndex++] = (char) ((jchar >> 0xA) + 0xD7C0);
|
||||
cArr[outIndex++] = (char) ((jchar & 0x3FF) + 0xDC00);
|
||||
outRemaining -= 2;
|
||||
}
|
||||
}
|
||||
in.position(inIndex - in.arrayOffset());
|
||||
out.position(outIndex - out.arrayOffset());
|
||||
return (outRemaining == 0 && inIndex < inIndexLimit) ?
|
||||
CoderResult.OVERFLOW :
|
||||
CoderResult.UNDERFLOW;
|
||||
}
|
||||
}
|
||||
235
java/org/apache/tomcat/util/buf/Utf8Encoder.java
Normal file
235
java/org/apache/tomcat/util/buf/Utf8Encoder.java
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.buf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Encodes characters as bytes using UTF-8. Extracted from Apache Harmony with
|
||||
* some minor bug fixes applied.
|
||||
*/
|
||||
public class Utf8Encoder extends CharsetEncoder {
|
||||
|
||||
public Utf8Encoder() {
|
||||
super(StandardCharsets.UTF_8, 1.1f, 4.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
|
||||
if (in.hasArray() && out.hasArray()) {
|
||||
return encodeHasArray(in, out);
|
||||
}
|
||||
return encodeNotHasArray(in, out);
|
||||
}
|
||||
|
||||
private CoderResult encodeHasArray(CharBuffer in, ByteBuffer out) {
|
||||
int outRemaining = out.remaining();
|
||||
int pos = in.position();
|
||||
int limit = in.limit();
|
||||
byte[] bArr;
|
||||
char[] cArr;
|
||||
int x = pos;
|
||||
bArr = out.array();
|
||||
cArr = in.array();
|
||||
int outPos = out.position();
|
||||
int rem = in.remaining();
|
||||
for (x = pos; x < pos + rem; x++) {
|
||||
int jchar = (cArr[x] & 0xFFFF);
|
||||
|
||||
if (jchar <= 0x7F) {
|
||||
if (outRemaining < 1) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
bArr[outPos++] = (byte) (jchar & 0xFF);
|
||||
outRemaining--;
|
||||
} else if (jchar <= 0x7FF) {
|
||||
|
||||
if (outRemaining < 2) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
bArr[outPos++] = (byte) (0xC0 + ((jchar >> 6) & 0x1F));
|
||||
bArr[outPos++] = (byte) (0x80 + (jchar & 0x3F));
|
||||
outRemaining -= 2;
|
||||
|
||||
} else if (jchar >= 0xD800 && jchar <= 0xDFFF) {
|
||||
|
||||
// in has to have one byte more.
|
||||
if (limit <= x + 1) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.UNDERFLOW;
|
||||
}
|
||||
|
||||
if (outRemaining < 4) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
|
||||
// The surrogate pair starts with a low-surrogate.
|
||||
if (jchar >= 0xDC00) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
|
||||
int jchar2 = cArr[x + 1] & 0xFFFF;
|
||||
|
||||
// The surrogate pair ends with a high-surrogate.
|
||||
if (jchar2 < 0xDC00) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
|
||||
// Note, the Unicode scalar value n is defined
|
||||
// as follows:
|
||||
// n = (jchar-0xD800)*0x400+(jchar2-0xDC00)+0x10000
|
||||
// Where jchar is a high-surrogate,
|
||||
// jchar2 is a low-surrogate.
|
||||
int n = (jchar << 10) + jchar2 + 0xFCA02400;
|
||||
|
||||
bArr[outPos++] = (byte) (0xF0 + ((n >> 18) & 0x07));
|
||||
bArr[outPos++] = (byte) (0x80 + ((n >> 12) & 0x3F));
|
||||
bArr[outPos++] = (byte) (0x80 + ((n >> 6) & 0x3F));
|
||||
bArr[outPos++] = (byte) (0x80 + (n & 0x3F));
|
||||
outRemaining -= 4;
|
||||
x++;
|
||||
|
||||
} else {
|
||||
|
||||
if (outRemaining < 3) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
bArr[outPos++] = (byte) (0xE0 + ((jchar >> 12) & 0x0F));
|
||||
bArr[outPos++] = (byte) (0x80 + ((jchar >> 6) & 0x3F));
|
||||
bArr[outPos++] = (byte) (0x80 + (jchar & 0x3F));
|
||||
outRemaining -= 3;
|
||||
}
|
||||
if (outRemaining == 0) {
|
||||
in.position(x + 1);
|
||||
out.position(outPos);
|
||||
// If both input and output are exhausted, return UNDERFLOW
|
||||
if (x + 1 == limit) {
|
||||
return CoderResult.UNDERFLOW;
|
||||
} else {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (rem != 0) {
|
||||
in.position(x);
|
||||
out.position(outPos);
|
||||
}
|
||||
return CoderResult.UNDERFLOW;
|
||||
}
|
||||
|
||||
private CoderResult encodeNotHasArray(CharBuffer in, ByteBuffer out) {
|
||||
int outRemaining = out.remaining();
|
||||
int pos = in.position();
|
||||
int limit = in.limit();
|
||||
try {
|
||||
while (pos < limit) {
|
||||
if (outRemaining == 0) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
|
||||
int jchar = (in.get() & 0xFFFF);
|
||||
|
||||
if (jchar <= 0x7F) {
|
||||
|
||||
if (outRemaining < 1) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
out.put((byte) jchar);
|
||||
outRemaining--;
|
||||
|
||||
} else if (jchar <= 0x7FF) {
|
||||
|
||||
if (outRemaining < 2) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
out.put((byte) (0xC0 + ((jchar >> 6) & 0x1F)));
|
||||
out.put((byte) (0x80 + (jchar & 0x3F)));
|
||||
outRemaining -= 2;
|
||||
|
||||
} else if (jchar >= 0xD800 && jchar <= 0xDFFF) {
|
||||
|
||||
// in has to have one byte more.
|
||||
if (limit <= pos + 1) {
|
||||
return CoderResult.UNDERFLOW;
|
||||
}
|
||||
|
||||
if (outRemaining < 4) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
|
||||
// The surrogate pair starts with a low-surrogate.
|
||||
if (jchar >= 0xDC00) {
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
|
||||
int jchar2 = (in.get() & 0xFFFF);
|
||||
|
||||
// The surrogate pair ends with a high-surrogate.
|
||||
if (jchar2 < 0xDC00) {
|
||||
return CoderResult.malformedForLength(1);
|
||||
}
|
||||
|
||||
// Note, the Unicode scalar value n is defined
|
||||
// as follows:
|
||||
// n = (jchar-0xD800)*0x400+(jchar2-0xDC00)+0x10000
|
||||
// Where jchar is a high-surrogate,
|
||||
// jchar2 is a low-surrogate.
|
||||
int n = (jchar << 10) + jchar2 + 0xFCA02400;
|
||||
|
||||
out.put((byte) (0xF0 + ((n >> 18) & 0x07)));
|
||||
out.put((byte) (0x80 + ((n >> 12) & 0x3F)));
|
||||
out.put((byte) (0x80 + ((n >> 6) & 0x3F)));
|
||||
out.put((byte) (0x80 + (n & 0x3F)));
|
||||
outRemaining -= 4;
|
||||
pos++;
|
||||
|
||||
} else {
|
||||
|
||||
if (outRemaining < 3) {
|
||||
return CoderResult.OVERFLOW;
|
||||
}
|
||||
out.put((byte) (0xE0 + ((jchar >> 12) & 0x0F)));
|
||||
out.put((byte) (0x80 + ((jchar >> 6) & 0x3F)));
|
||||
out.put((byte) (0x80 + (jchar & 0x3F)));
|
||||
outRemaining -= 3;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
} finally {
|
||||
in.position(pos);
|
||||
}
|
||||
return CoderResult.UNDERFLOW;
|
||||
}
|
||||
}
|
||||
37
java/org/apache/tomcat/util/buf/package.html
Normal file
37
java/org/apache/tomcat/util/buf/package.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html><body>
|
||||
<H2>Buffers and Encodings</h2>
|
||||
|
||||
This package contains buffers and utils to perform encoding/decoding of buffers. That includes byte to char
|
||||
conversions, URL encodings, etc.
|
||||
|
||||
<p>
|
||||
Encoding is a critical operation for performance. There are few tricks in this package - the C2B and
|
||||
B2C converters are caching an ISReader/OSWriter and keep everything allocated to do the conversions
|
||||
in any VM without any garbage.
|
||||
|
||||
<p>
|
||||
This package must accommodate future extensions and additional converters ( most important: the nio.charset,
|
||||
which should be detected and used if available ). Also, we do have one hand-written UTF8Decoder, and
|
||||
other tuned encoders could be added.
|
||||
|
||||
<p>
|
||||
My benchmarks ( I'm costin :-) show only small differences between C2B, B2C and hand-written codders/decoders,
|
||||
so UTF8Decoder may be disabled.
|
||||
|
||||
</body></html>
|
||||
38
java/org/apache/tomcat/util/codec/BinaryDecoder.java
Normal file
38
java/org/apache/tomcat/util/codec/BinaryDecoder.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec;
|
||||
|
||||
/**
|
||||
* Defines common decoding methods for byte array decoders.
|
||||
*
|
||||
* @deprecated This interface is unused and will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BinaryDecoder extends Decoder {
|
||||
|
||||
/**
|
||||
* Decodes a byte array and returns the results as a byte array.
|
||||
*
|
||||
* @param source
|
||||
* A byte array which has been encoded with the appropriate encoder
|
||||
* @return a byte array that contains decoded content
|
||||
* @throws DecoderException
|
||||
* A decoder exception is thrown if a Decoder encounters a failure condition during the decode process.
|
||||
*/
|
||||
byte[] decode(byte[] source) throws DecoderException;
|
||||
}
|
||||
|
||||
38
java/org/apache/tomcat/util/codec/BinaryEncoder.java
Normal file
38
java/org/apache/tomcat/util/codec/BinaryEncoder.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec;
|
||||
|
||||
/**
|
||||
* Defines common encoding methods for byte array encoders.
|
||||
*
|
||||
* @deprecated This interface is unused and will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BinaryEncoder extends Encoder {
|
||||
|
||||
/**
|
||||
* Encodes a byte array and return the encoded data as a byte array.
|
||||
*
|
||||
* @param source
|
||||
* Data to be encoded
|
||||
* @return A byte array containing the encoded data
|
||||
* @throws EncoderException
|
||||
* thrown if the Encoder encounters a failure condition during the encoding process.
|
||||
*/
|
||||
byte[] encode(byte[] source) throws EncoderException;
|
||||
}
|
||||
|
||||
47
java/org/apache/tomcat/util/codec/Decoder.java
Normal file
47
java/org/apache/tomcat/util/codec/Decoder.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec;
|
||||
|
||||
/**
|
||||
* Provides the highest level of abstraction for Decoders.
|
||||
* <p>
|
||||
* This is the sister interface of {@link Encoder}. All Decoders implement this common generic interface.
|
||||
* Allows a user to pass a generic Object to any Decoder implementation in the codec package.
|
||||
* <p>
|
||||
* One of the two interfaces at the center of the codec package.
|
||||
*
|
||||
* @deprecated This interface is unused and will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public interface Decoder {
|
||||
|
||||
/**
|
||||
* Decodes an "encoded" Object and returns a "decoded" Object. Note that the implementation of this interface will
|
||||
* try to cast the Object parameter to the specific type expected by a particular Decoder implementation. If a
|
||||
* {@link ClassCastException} occurs this decode method will throw a DecoderException.
|
||||
*
|
||||
* @param source
|
||||
* the object to decode
|
||||
* @return a 'decoded" object
|
||||
* @throws DecoderException
|
||||
* a decoder exception can be thrown for any number of reasons. Some good candidates are that the
|
||||
* parameter passed to this method is null, a param cannot be cast to the appropriate type for a
|
||||
* specific encoder.
|
||||
*/
|
||||
Object decode(Object source) throws DecoderException;
|
||||
}
|
||||
|
||||
86
java/org/apache/tomcat/util/codec/DecoderException.java
Normal file
86
java/org/apache/tomcat/util/codec/DecoderException.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec;
|
||||
|
||||
/**
|
||||
* Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder}
|
||||
* encounters a decoding specific exception such as invalid data, or characters outside of the expected range.
|
||||
*
|
||||
* @deprecated This exception is unused and will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public class DecoderException extends Exception {
|
||||
|
||||
/**
|
||||
* Declares the Serial Version Uid.
|
||||
*
|
||||
* @see <a href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always Declare Serial Version Uid</a>
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs a new exception with <code>null</code> as its detail message. The cause is not initialized, and may
|
||||
* subsequently be initialized by a call to {@link #initCause}.
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
public DecoderException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently
|
||||
* be initialized by a call to {@link #initCause}.
|
||||
*
|
||||
* @param message
|
||||
* The detail message which is saved for later retrieval by the {@link #getMessage()} method.
|
||||
*/
|
||||
public DecoderException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified detail message and cause.
|
||||
* <p>
|
||||
* Note that the detail message associated with <code>cause</code> is not automatically incorporated into this
|
||||
* exception's detail message.
|
||||
*
|
||||
* @param message
|
||||
* The detail message which is saved for later retrieval by the {@link #getMessage()} method.
|
||||
* @param cause
|
||||
* The cause which is saved for later retrieval by the {@link #getCause()} method. A <code>null</code>
|
||||
* value is permitted, and indicates that the cause is nonexistent or unknown.
|
||||
* @since 1.4
|
||||
*/
|
||||
public DecoderException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified cause and a detail message of <code>(cause==null ?
|
||||
* null : cause.toString())</code> (which typically contains the class and detail message of <code>cause</code>).
|
||||
* This constructor is useful for exceptions that are little more than wrappers for other throwables.
|
||||
*
|
||||
* @param cause
|
||||
* The cause which is saved for later retrieval by the {@link #getCause()} method. A <code>null</code>
|
||||
* value is permitted, and indicates that the cause is nonexistent or unknown.
|
||||
* @since 1.4
|
||||
*/
|
||||
public DecoderException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
44
java/org/apache/tomcat/util/codec/Encoder.java
Normal file
44
java/org/apache/tomcat/util/codec/Encoder.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec;
|
||||
|
||||
/**
|
||||
* Provides the highest level of abstraction for Encoders.
|
||||
* <p>
|
||||
* This is the sister interface of {@link Decoder}. Every implementation of Encoder provides this
|
||||
* common generic interface which allows a user to pass a generic Object to any Encoder implementation
|
||||
* in the codec package.
|
||||
*
|
||||
* @deprecated This interface is unused and will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public interface Encoder {
|
||||
|
||||
/**
|
||||
* Encodes an "Object" and returns the encoded content as an Object. The Objects here may just be
|
||||
* <code>byte[]</code> or <code>String</code>s depending on the implementation used.
|
||||
*
|
||||
* @param source
|
||||
* An object to encode
|
||||
* @return An "encoded" Object
|
||||
* @throws EncoderException
|
||||
* An encoder exception is thrown if the encoder experiences a failure condition during the encoding
|
||||
* process.
|
||||
*/
|
||||
Object encode(Object source) throws EncoderException;
|
||||
}
|
||||
|
||||
89
java/org/apache/tomcat/util/codec/EncoderException.java
Normal file
89
java/org/apache/tomcat/util/codec/EncoderException.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec;
|
||||
|
||||
/**
|
||||
* Thrown when there is a failure condition during the encoding process. This exception is thrown when an
|
||||
* {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum,
|
||||
* characters outside of the expected range.
|
||||
*
|
||||
* @deprecated This exception is unused and will be removed in Tomcat 9
|
||||
*/
|
||||
@Deprecated
|
||||
public class EncoderException extends Exception {
|
||||
|
||||
/**
|
||||
* Declares the Serial Version Uid.
|
||||
*
|
||||
* @see <a href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always Declare Serial Version Uid</a>
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs a new exception with <code>null</code> as its detail message. The cause is not initialized, and may
|
||||
* subsequently be initialized by a call to {@link #initCause}.
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
public EncoderException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently
|
||||
* be initialized by a call to {@link #initCause}.
|
||||
*
|
||||
* @param message
|
||||
* a useful message relating to the encoder specific error.
|
||||
*/
|
||||
public EncoderException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified detail message and cause.
|
||||
*
|
||||
* <p>
|
||||
* Note that the detail message associated with <code>cause</code> is not automatically incorporated into this
|
||||
* exception's detail message.
|
||||
* </p>
|
||||
*
|
||||
* @param message
|
||||
* The detail message which is saved for later retrieval by the {@link #getMessage()} method.
|
||||
* @param cause
|
||||
* The cause which is saved for later retrieval by the {@link #getCause()} method. A <code>null</code>
|
||||
* value is permitted, and indicates that the cause is nonexistent or unknown.
|
||||
* @since 1.4
|
||||
*/
|
||||
public EncoderException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified cause and a detail message of <code>(cause==null ?
|
||||
* null : cause.toString())</code> (which typically contains the class and detail message of <code>cause</code>).
|
||||
* This constructor is useful for exceptions that are little more than wrappers for other throwables.
|
||||
*
|
||||
* @param cause
|
||||
* The cause which is saved for later retrieval by the {@link #getCause()} method. A <code>null</code>
|
||||
* value is permitted, and indicates that the cause is nonexistent or unknown.
|
||||
* @since 1.4
|
||||
*/
|
||||
public EncoderException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
817
java/org/apache/tomcat/util/codec/binary/Base64.java
Normal file
817
java/org/apache/tomcat/util/codec/binary/Base64.java
Normal file
@@ -0,0 +1,817 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec.binary;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Provides Base64 encoding and decoding as defined by <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
|
||||
*
|
||||
* <p>
|
||||
* This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
|
||||
* Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
|
||||
* </p>
|
||||
* <p>
|
||||
* The class can be parameterized in the following manner with various constructors:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>URL-safe mode: Default off.</li>
|
||||
* <li>Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of
|
||||
* 4 in the encoded data.
|
||||
* <li>Line separator: Default is CRLF ("\r\n")</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The URL-safe parameter is only applied to encode operations. Decoding seamlessly handles both modes.
|
||||
* </p>
|
||||
* <p>
|
||||
* Since this class operates directly on byte streams, and not character streams, it is hard-coded to only
|
||||
* encode/decode character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252,
|
||||
* UTF-8, etc).
|
||||
* </p>
|
||||
* <p>
|
||||
* This class is thread-safe.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Base64 extends BaseNCodec {
|
||||
|
||||
/**
|
||||
* BASE32 characters are 6 bits in length.
|
||||
* They are formed by taking a block of 3 octets to form a 24-bit string,
|
||||
* which is converted into 4 BASE64 characters.
|
||||
*/
|
||||
private static final int BITS_PER_ENCODED_BYTE = 6;
|
||||
private static final int BYTES_PER_UNENCODED_BLOCK = 3;
|
||||
private static final int BYTES_PER_ENCODED_BLOCK = 4;
|
||||
|
||||
/**
|
||||
* Chunk separator per RFC 2045 section 2.1.
|
||||
*
|
||||
* <p>
|
||||
* N.B. The next major release may break compatibility and make this field private.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
|
||||
*/
|
||||
static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
|
||||
|
||||
/**
|
||||
* This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet"
|
||||
* equivalents as specified in Table 1 of RFC 2045.
|
||||
*
|
||||
* Thanks to "commons" project in ws.apache.org for this code.
|
||||
* https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
|
||||
*/
|
||||
private static final byte[] STANDARD_ENCODE_TABLE = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a copy of the STANDARD_ENCODE_TABLE above, but with + and /
|
||||
* changed to - and _ to make the encoded Base64 results more URL-SAFE.
|
||||
* This table is only used when the Base64's mode is set to URL-SAFE.
|
||||
*/
|
||||
private static final byte[] URL_SAFE_ENCODE_TABLE = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
|
||||
};
|
||||
|
||||
/**
|
||||
* This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified
|
||||
* in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64
|
||||
* alphabet but fall within the bounds of the array are translated to -1.
|
||||
*
|
||||
* Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both
|
||||
* URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit).
|
||||
*
|
||||
* Thanks to "commons" project in ws.apache.org for this code.
|
||||
* https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
|
||||
*/
|
||||
private static final byte[] STANDARD_DECODE_TABLE = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 20-2f + /
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 50-5f P-Z
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z
|
||||
};
|
||||
|
||||
private static final byte[] URL_SAFE_DECODE_TABLE = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, // 20-2f -
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z
|
||||
};
|
||||
|
||||
/**
|
||||
* Base64 uses 6-bit fields.
|
||||
*/
|
||||
/** Mask used to extract 6 bits, used when encoding */
|
||||
private static final int MASK_6BITS = 0x3f;
|
||||
/** Mask used to extract 4 bits, used when decoding final trailing character. */
|
||||
private static final int MASK_4BITS = 0xf;
|
||||
/** Mask used to extract 2 bits, used when decoding final trailing character. */
|
||||
private static final int MASK_2BITS = 0x3;
|
||||
|
||||
// The static final fields above are used for the original static byte[] methods on Base64.
|
||||
// The private member fields below are used with the new streaming approach, which requires
|
||||
// some state be preserved between calls of encode() and decode().
|
||||
|
||||
/**
|
||||
* Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able
|
||||
* to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch
|
||||
* between the two modes.
|
||||
*/
|
||||
private final byte[] encodeTable;
|
||||
|
||||
// Only one decode table currently; keep for consistency with Base32 code
|
||||
private final byte[] decodeTable;
|
||||
|
||||
/**
|
||||
* Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
|
||||
*/
|
||||
private final byte[] lineSeparator;
|
||||
|
||||
/**
|
||||
* Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
|
||||
* <code>decodeSize = 3 + lineSeparator.length;</code>
|
||||
*/
|
||||
private final int decodeSize;
|
||||
|
||||
/**
|
||||
* Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
|
||||
* <code>encodeSize = 4 + lineSeparator.length;</code>
|
||||
*/
|
||||
private final int encodeSize;
|
||||
|
||||
/**
|
||||
* Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
|
||||
* <p>
|
||||
* When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When decoding all variants are supported.
|
||||
* </p>
|
||||
*/
|
||||
public Base64() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode.
|
||||
* <p>
|
||||
* When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When decoding all variants are supported.
|
||||
* </p>
|
||||
*
|
||||
* @param urlSafe
|
||||
* if <code>true</code>, URL-safe encoding is used. In most cases this should be set to
|
||||
* <code>false</code>.
|
||||
* @since 1.4
|
||||
*/
|
||||
public Base64(final boolean urlSafe) {
|
||||
this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
|
||||
* <p>
|
||||
* When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is
|
||||
* STANDARD_ENCODE_TABLE.
|
||||
* </p>
|
||||
* <p>
|
||||
* Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
|
||||
* </p>
|
||||
* <p>
|
||||
* When decoding all variants are supported.
|
||||
* </p>
|
||||
*
|
||||
* @param lineLength
|
||||
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
|
||||
* 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when
|
||||
* decoding.
|
||||
* @since 1.4
|
||||
*/
|
||||
public Base64(final int lineLength) {
|
||||
this(lineLength, CHUNK_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
|
||||
* <p>
|
||||
* When encoding the line length and line separator are given in the constructor, and the encoding table is
|
||||
* STANDARD_ENCODE_TABLE.
|
||||
* </p>
|
||||
* <p>
|
||||
* Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
|
||||
* </p>
|
||||
* <p>
|
||||
* When decoding all variants are supported.
|
||||
* </p>
|
||||
*
|
||||
* @param lineLength
|
||||
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
|
||||
* 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when
|
||||
* decoding.
|
||||
* @param lineSeparator
|
||||
* Each line of encoded data will end with this sequence of bytes.
|
||||
* @throws IllegalArgumentException
|
||||
* Thrown when the provided lineSeparator included some base64 characters.
|
||||
* @since 1.4
|
||||
*/
|
||||
public Base64(final int lineLength, final byte[] lineSeparator) {
|
||||
this(lineLength, lineSeparator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
|
||||
* <p>
|
||||
* When encoding the line length and line separator are given in the constructor, and the encoding table is
|
||||
* STANDARD_ENCODE_TABLE.
|
||||
* </p>
|
||||
* <p>
|
||||
* Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
|
||||
* </p>
|
||||
* <p>
|
||||
* When decoding all variants are supported.
|
||||
* </p>
|
||||
*
|
||||
* @param lineLength
|
||||
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
|
||||
* 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when
|
||||
* decoding.
|
||||
* @param lineSeparator
|
||||
* Each line of encoded data will end with this sequence of bytes.
|
||||
* @param urlSafe
|
||||
* Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode
|
||||
* operations. Decoding seamlessly handles both modes.
|
||||
* <b>Note: no padding is added when using the URL-safe alphabet.</b>
|
||||
* @throws IllegalArgumentException
|
||||
* The provided lineSeparator included some base64 characters. That's not going to work!
|
||||
* @since 1.4
|
||||
*/
|
||||
public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) {
|
||||
super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
|
||||
lineLength,
|
||||
lineSeparator == null ? 0 : lineSeparator.length);
|
||||
// Needs to be set early to avoid NPE during call to containsAlphabetOrPad() below
|
||||
this.decodeTable = urlSafe ? URL_SAFE_DECODE_TABLE : STANDARD_DECODE_TABLE;
|
||||
// TODO could be simplified if there is no requirement to reject invalid line sep when length <=0
|
||||
// @see test case Base64Test.testConstructors()
|
||||
if (lineSeparator != null) {
|
||||
if (containsAlphabetOrPad(lineSeparator)) {
|
||||
final String sep = StringUtils.newStringUtf8(lineSeparator);
|
||||
throw new IllegalArgumentException(sm.getString("base64.lineSeparator", sep));
|
||||
}
|
||||
if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE
|
||||
this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length;
|
||||
this.lineSeparator = new byte[lineSeparator.length];
|
||||
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
|
||||
} else {
|
||||
this.encodeSize = BYTES_PER_ENCODED_BLOCK;
|
||||
this.lineSeparator = null;
|
||||
}
|
||||
} else {
|
||||
this.encodeSize = BYTES_PER_ENCODED_BLOCK;
|
||||
this.lineSeparator = null;
|
||||
}
|
||||
this.decodeSize = this.encodeSize - 1;
|
||||
this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our current encode mode. True if we're URL-SAFE, false otherwise.
|
||||
*
|
||||
* @return true if we're in URL-SAFE mode, false otherwise.
|
||||
* @since 1.4
|
||||
*/
|
||||
public boolean isUrlSafe() {
|
||||
return this.encodeTable == URL_SAFE_ENCODE_TABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with
|
||||
* the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, to flush last
|
||||
* remaining bytes (if not multiple of 3).
|
||||
* </p>
|
||||
* <p><b>Note: no padding is added when encoding using the URL-safe alphabet.</b></p>
|
||||
* <p>
|
||||
* Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
|
||||
* https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
|
||||
* </p>
|
||||
*
|
||||
* @param in
|
||||
* byte[] array of binary data to base64 encode.
|
||||
* @param inPos
|
||||
* Position to start reading data from.
|
||||
* @param inAvail
|
||||
* Amount of bytes available from input for encoding.
|
||||
* @param context
|
||||
* the context to be used
|
||||
*/
|
||||
@Override
|
||||
void encode(final byte[] in, int inPos, final int inAvail, final Context context) {
|
||||
if (context.eof) {
|
||||
return;
|
||||
}
|
||||
// inAvail < 0 is how we're informed of EOF in the underlying data we're
|
||||
// encoding.
|
||||
if (inAvail < 0) {
|
||||
context.eof = true;
|
||||
if (0 == context.modulus && lineLength == 0) {
|
||||
return; // no leftovers to process and not using chunking
|
||||
}
|
||||
final byte[] buffer = ensureBufferSize(encodeSize, context);
|
||||
final int savedPos = context.pos;
|
||||
switch (context.modulus) { // 0-2
|
||||
case 0 : // nothing to do here
|
||||
break;
|
||||
case 1 : // 8 bits = 6 + 2
|
||||
// top 6 bits:
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS];
|
||||
// remaining 2:
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS];
|
||||
// URL-SAFE skips the padding to further reduce size.
|
||||
if (encodeTable == STANDARD_ENCODE_TABLE) {
|
||||
buffer[context.pos++] = pad;
|
||||
buffer[context.pos++] = pad;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2 : // 16 bits = 6 + 6 + 4
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS];
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS];
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS];
|
||||
// URL-SAFE skips the padding to further reduce size.
|
||||
if (encodeTable == STANDARD_ENCODE_TABLE) {
|
||||
buffer[context.pos++] = pad;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(sm.getString(
|
||||
"base64.impossibleModulus", Integer.valueOf(context.modulus)));
|
||||
}
|
||||
context.currentLinePos += context.pos - savedPos; // keep track of current line position
|
||||
// if currentPos == 0 we are at the start of a line, so don't add CRLF
|
||||
if (lineLength > 0 && context.currentLinePos > 0) {
|
||||
System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
|
||||
context.pos += lineSeparator.length;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < inAvail; i++) {
|
||||
final byte[] buffer = ensureBufferSize(encodeSize, context);
|
||||
context.modulus = (context.modulus+1) % BYTES_PER_UNENCODED_BLOCK;
|
||||
int b = in[inPos++];
|
||||
if (b < 0) {
|
||||
b += 256;
|
||||
}
|
||||
context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE
|
||||
if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS];
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS];
|
||||
buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS];
|
||||
buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS];
|
||||
context.currentLinePos += BYTES_PER_ENCODED_BLOCK;
|
||||
if (lineLength > 0 && lineLength <= context.currentLinePos) {
|
||||
System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
|
||||
context.pos += lineSeparator.length;
|
||||
context.currentLinePos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once
|
||||
* with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1"
|
||||
* call is not necessary when decoding, but it doesn't hurt, either.
|
||||
* </p>
|
||||
* <p>
|
||||
* Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are
|
||||
* silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in,
|
||||
* garbage-out philosophy: it will not check the provided data for validity.
|
||||
* </p>
|
||||
* <p>
|
||||
* Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
|
||||
* https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
|
||||
* </p>
|
||||
*
|
||||
* @param in
|
||||
* byte[] array of ascii data to base64 decode.
|
||||
* @param inPos
|
||||
* Position to start reading data from.
|
||||
* @param inAvail
|
||||
* Amount of bytes available from input for decoding.
|
||||
* @param context
|
||||
* the context to be used
|
||||
*/
|
||||
@Override
|
||||
void decode(final byte[] in, int inPos, final int inAvail, final Context context) {
|
||||
if (context.eof) {
|
||||
return;
|
||||
}
|
||||
if (inAvail < 0) {
|
||||
context.eof = true;
|
||||
}
|
||||
for (int i = 0; i < inAvail; i++) {
|
||||
final byte[] buffer = ensureBufferSize(decodeSize, context);
|
||||
final byte b = in[inPos++];
|
||||
if (b == pad) {
|
||||
// We're done.
|
||||
context.eof = true;
|
||||
break;
|
||||
}
|
||||
if (b >= 0 && b < decodeTable.length) {
|
||||
final int result = decodeTable[b];
|
||||
if (result >= 0) {
|
||||
context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK;
|
||||
context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;
|
||||
if (context.modulus == 0) {
|
||||
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS);
|
||||
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
|
||||
buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Two forms of EOF as far as base64 decoder is concerned: actual
|
||||
// EOF (-1) and first time '=' character is encountered in stream.
|
||||
// This approach makes the '=' padding characters completely optional.
|
||||
if (context.eof && context.modulus != 0) {
|
||||
final byte[] buffer = ensureBufferSize(decodeSize, context);
|
||||
|
||||
// We have some spare bits remaining
|
||||
// Output all whole multiples of 8 bits and ignore the rest
|
||||
switch (context.modulus) {
|
||||
// case 0 : // impossible, as excluded above
|
||||
case 1 : // 6 bits - ignore entirely
|
||||
// TODO not currently tested; perhaps it is impossible?
|
||||
break;
|
||||
case 2 : // 12 bits = 8 + 4
|
||||
validateCharacter(MASK_4BITS, context);
|
||||
context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
|
||||
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
|
||||
break;
|
||||
case 3 : // 18 bits = 8 + 8 + 2
|
||||
validateCharacter(MASK_2BITS, context);
|
||||
context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
|
||||
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
|
||||
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(sm.getString(
|
||||
"base64.impossibleModulus", Integer.valueOf(context.modulus)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the <code>octet</code> is in the base 64 alphabet.
|
||||
*
|
||||
* @param octet
|
||||
* The value to test
|
||||
* @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
|
||||
* @since 1.4
|
||||
*/
|
||||
public static boolean isBase64(final byte octet) {
|
||||
return octet == PAD_DEFAULT || (octet >= 0 && octet < STANDARD_DECODE_TABLE.length && STANDARD_DECODE_TABLE[octet] != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the
|
||||
* method treats whitespace as valid.
|
||||
*
|
||||
* @param base64
|
||||
* String to test
|
||||
* @return <code>true</code> if all characters in the String are valid characters in the Base64 alphabet or if
|
||||
* the String is empty; <code>false</code>, otherwise
|
||||
* @since 1.5
|
||||
*/
|
||||
public static boolean isBase64(final String base64) {
|
||||
return isBase64(StringUtils.getBytesUtf8(base64));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the
|
||||
* method treats whitespace as valid.
|
||||
*
|
||||
* @param arrayOctet
|
||||
* byte array to test
|
||||
* @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is empty;
|
||||
* <code>false</code>, otherwise
|
||||
* @since 1.5
|
||||
*/
|
||||
public static boolean isBase64(final byte[] arrayOctet) {
|
||||
for (int i = 0; i < arrayOctet.length; i++) {
|
||||
if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using the base64 algorithm but does not chunk the output.
|
||||
*
|
||||
* @param binaryData
|
||||
* binary data to encode
|
||||
* @return byte[] containing Base64 characters in their UTF-8 representation.
|
||||
*/
|
||||
public static byte[] encodeBase64(final byte[] binaryData) {
|
||||
return encodeBase64(binaryData, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using the base64 algorithm but does not chunk the output.
|
||||
*
|
||||
* NOTE: We changed the behaviour of this method from multi-line chunking (commons-codec-1.4) to
|
||||
* single-line non-chunking (commons-codec-1.5).
|
||||
*
|
||||
* @param binaryData
|
||||
* binary data to encode
|
||||
* @return String containing Base64 characters.
|
||||
* @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not).
|
||||
*/
|
||||
public static String encodeBase64String(final byte[] binaryData) {
|
||||
return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
|
||||
* url-safe variation emits - and _ instead of + and / characters.
|
||||
* <b>Note: no padding is added.</b>
|
||||
* @param binaryData
|
||||
* binary data to encode
|
||||
* @return byte[] containing Base64 characters in their UTF-8 representation.
|
||||
* @since 1.4
|
||||
*/
|
||||
public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
|
||||
return encodeBase64(binaryData, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
|
||||
* url-safe variation emits - and _ instead of + and / characters.
|
||||
* <b>Note: no padding is added.</b>
|
||||
* @param binaryData
|
||||
* binary data to encode
|
||||
* @return String containing Base64 characters
|
||||
* @since 1.4
|
||||
*/
|
||||
public static String encodeBase64URLSafeString(final byte[] binaryData) {
|
||||
return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
|
||||
*
|
||||
* @param binaryData
|
||||
* binary data to encode
|
||||
* @return Base64 characters chunked in 76 character blocks
|
||||
*/
|
||||
public static byte[] encodeBase64Chunked(final byte[] binaryData) {
|
||||
return encodeBase64(binaryData, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
|
||||
*
|
||||
* @param binaryData
|
||||
* Array containing binary data to encode.
|
||||
* @param isChunked
|
||||
* if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
|
||||
* @return Base64-encoded data.
|
||||
* @throws IllegalArgumentException
|
||||
* Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
|
||||
*/
|
||||
public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) {
|
||||
return encodeBase64(binaryData, isChunked, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
|
||||
*
|
||||
* @param binaryData
|
||||
* Array containing binary data to encode.
|
||||
* @param isChunked
|
||||
* if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
|
||||
* @param urlSafe
|
||||
* if <code>true</code> this encoder will emit - and _ instead of the usual + and / characters.
|
||||
* <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>
|
||||
* @return Base64-encoded data.
|
||||
* @throws IllegalArgumentException
|
||||
* Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
|
||||
* @since 1.4
|
||||
*/
|
||||
public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {
|
||||
return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
|
||||
*
|
||||
* @param binaryData
|
||||
* Array containing binary data to encode.
|
||||
* @param isChunked
|
||||
* if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
|
||||
* @param urlSafe
|
||||
* if <code>true</code> this encoder will emit - and _ instead of the usual + and / characters.
|
||||
* <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>
|
||||
* @param maxResultSize
|
||||
* The maximum result size to accept.
|
||||
* @return Base64-encoded data.
|
||||
* @throws IllegalArgumentException
|
||||
* Thrown when the input array needs an output array bigger than maxResultSize
|
||||
* @since 1.4
|
||||
*/
|
||||
public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked,
|
||||
final boolean urlSafe, final int maxResultSize) {
|
||||
if (binaryData == null || binaryData.length == 0) {
|
||||
return binaryData;
|
||||
}
|
||||
|
||||
// Create this so can use the super-class method
|
||||
// Also ensures that the same roundings are performed by the ctor and the code
|
||||
final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
|
||||
final long len = b64.getEncodedLength(binaryData);
|
||||
if (len > maxResultSize) {
|
||||
throw new IllegalArgumentException(sm.getString(
|
||||
"base64.inputTooLarge", Long.valueOf(len), Integer.valueOf(maxResultSize)));
|
||||
}
|
||||
|
||||
return b64.encode(binaryData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a Base64 String into octets.
|
||||
* <p>
|
||||
* <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
|
||||
* </p>
|
||||
*
|
||||
* @param base64String
|
||||
* String containing Base64 data
|
||||
* @return Array containing decoded data.
|
||||
* @since 1.4
|
||||
*/
|
||||
public static byte[] decodeBase64(final String base64String) {
|
||||
return new Base64().decode(base64String);
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64URLSafe(final String base64String) {
|
||||
return new Base64(true).decode(base64String);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64 data into octets.
|
||||
* <p>
|
||||
* <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
|
||||
* </p>
|
||||
*
|
||||
* @param base64Data
|
||||
* Byte array containing Base64 data
|
||||
* @return Array containing decoded data.
|
||||
*/
|
||||
public static byte[] decodeBase64(final byte[] base64Data) {
|
||||
return decodeBase64(base64Data, 0, base64Data.length);
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(
|
||||
final byte[] base64Data, final int off, final int len) {
|
||||
return new Base64().decode(base64Data, off, len);
|
||||
}
|
||||
|
||||
// Implementation of the Encoder Interface
|
||||
|
||||
// Implementation of integer encoding used for crypto
|
||||
/**
|
||||
* Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
|
||||
*
|
||||
* @param pArray
|
||||
* a byte array containing base64 character data
|
||||
* @return A BigInteger
|
||||
* @since 1.4
|
||||
*/
|
||||
public static BigInteger decodeInteger(final byte[] pArray) {
|
||||
return new BigInteger(1, decodeBase64(pArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
|
||||
*
|
||||
* @param bigInt
|
||||
* a BigInteger
|
||||
* @return A byte array containing base64 character data
|
||||
* @throws NullPointerException
|
||||
* if null is passed in
|
||||
* @since 1.4
|
||||
*/
|
||||
public static byte[] encodeInteger(final BigInteger bigInt) {
|
||||
if (bigInt == null) {
|
||||
throw new NullPointerException(sm.getString("base64.nullEncodeParameter"));
|
||||
}
|
||||
return encodeBase64(toIntegerBytes(bigInt), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte-array representation of a <code>BigInteger</code> without sign bit.
|
||||
*
|
||||
* @param bigInt
|
||||
* <code>BigInteger</code> to be converted
|
||||
* @return a byte array representation of the BigInteger parameter
|
||||
*/
|
||||
static byte[] toIntegerBytes(final BigInteger bigInt) {
|
||||
int bitlen = bigInt.bitLength();
|
||||
// round bitlen
|
||||
bitlen = ((bitlen + 7) >> 3) << 3;
|
||||
final byte[] bigBytes = bigInt.toByteArray();
|
||||
|
||||
if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
|
||||
return bigBytes;
|
||||
}
|
||||
// set up params for copying everything but sign bit
|
||||
int startSrc = 0;
|
||||
int len = bigBytes.length;
|
||||
|
||||
// if bigInt is exactly byte-aligned, just skip signbit in copy
|
||||
if ((bigInt.bitLength() % 8) == 0) {
|
||||
startSrc = 1;
|
||||
len--;
|
||||
}
|
||||
final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
|
||||
final byte[] resizedBytes = new byte[bitlen / 8];
|
||||
System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
|
||||
return resizedBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the <code>octet</code> is in the Base64 alphabet.
|
||||
*
|
||||
* @param octet
|
||||
* The value to test
|
||||
* @return <code>true</code> if the value is defined in the the Base64 alphabet <code>false</code> otherwise.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isInAlphabet(final byte octet) {
|
||||
return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates whether decoding the final trailing character is possible in the context
|
||||
* of the set of possible base 64 values.
|
||||
*
|
||||
* <p>The character is valid if the lower bits within the provided mask are zero. This
|
||||
* is used to test the final trailing base-64 digit is zero in the bits that will be discarded.
|
||||
*
|
||||
* @param emptyBitsMask The mask of the lower bits that should be empty
|
||||
* @param context the context to be used
|
||||
*
|
||||
* @throws IllegalArgumentException if the bits being checked contain any non-zero value
|
||||
*/
|
||||
private static void validateCharacter(final int emptyBitsMask, final Context context) {
|
||||
if ((context.ibitWorkArea & emptyBitsMask) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value. " +
|
||||
"Expected the discarded bits to be zero.");
|
||||
}
|
||||
}
|
||||
}
|
||||
615
java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
Normal file
615
java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
Normal file
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec.binary;
|
||||
|
||||
import org.apache.tomcat.util.buf.HexUtils;
|
||||
import org.apache.tomcat.util.codec.BinaryDecoder;
|
||||
import org.apache.tomcat.util.codec.BinaryEncoder;
|
||||
import org.apache.tomcat.util.codec.DecoderException;
|
||||
import org.apache.tomcat.util.codec.EncoderException;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* Abstract superclass for Base-N encoders and decoders.
|
||||
*
|
||||
* <p>
|
||||
* This class is thread-safe.
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
|
||||
|
||||
protected static final StringManager sm = StringManager.getManager(BaseNCodec.class);
|
||||
|
||||
/**
|
||||
* Holds thread context so classes can be thread-safe.
|
||||
*
|
||||
* This class is not itself thread-safe; each thread must allocate its own copy.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
static class Context {
|
||||
|
||||
/**
|
||||
* Place holder for the bytes we're dealing with for our based logic.
|
||||
* Bitwise operations store and extract the encoding or decoding from this variable.
|
||||
*/
|
||||
int ibitWorkArea;
|
||||
|
||||
/**
|
||||
* Buffer for streaming.
|
||||
*/
|
||||
byte[] buffer;
|
||||
|
||||
/**
|
||||
* Position where next character should be written in the buffer.
|
||||
*/
|
||||
int pos;
|
||||
|
||||
/**
|
||||
* Position where next character should be read from the buffer.
|
||||
*/
|
||||
int readPos;
|
||||
|
||||
/**
|
||||
* Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless,
|
||||
* and must be thrown away.
|
||||
*/
|
||||
boolean eof;
|
||||
|
||||
/**
|
||||
* Variable tracks how many characters have been written to the current line. Only used when encoding. We use
|
||||
* it to make sure each encoded line never goes beyond lineLength (if lineLength > 0).
|
||||
*/
|
||||
int currentLinePos;
|
||||
|
||||
/**
|
||||
* Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This
|
||||
* variable helps track that.
|
||||
*/
|
||||
int modulus;
|
||||
|
||||
Context() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String useful for debugging (especially within a debugger.)
|
||||
*
|
||||
* @return a String useful for debugging.
|
||||
*/
|
||||
@SuppressWarnings("boxing") // OK to ignore boxing here
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, " +
|
||||
"ibitWorkArea=%s, modulus=%s, pos=%s, " +
|
||||
"readPos=%s]", this.getClass().getSimpleName(),
|
||||
HexUtils.toHexString(buffer), currentLinePos, eof,
|
||||
ibitWorkArea, modulus, pos, readPos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EOF
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
static final int EOF = -1;
|
||||
|
||||
/**
|
||||
* MIME chunk size per RFC 2045 section 6.8.
|
||||
*
|
||||
* <p>
|
||||
* The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
|
||||
* equal signs.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
|
||||
*/
|
||||
public static final int MIME_CHUNK_SIZE = 76;
|
||||
|
||||
/**
|
||||
* PEM chunk size per RFC 1421 section 4.3.2.4.
|
||||
*
|
||||
* <p>
|
||||
* The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
|
||||
* equal signs.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421 section 4.3.2.4</a>
|
||||
*/
|
||||
public static final int PEM_CHUNK_SIZE = 64;
|
||||
|
||||
private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;
|
||||
|
||||
/**
|
||||
* Defines the default buffer size - currently {@value}
|
||||
* - must be large enough for at least one encoded block+separator
|
||||
*/
|
||||
private static final int DEFAULT_BUFFER_SIZE = 128;
|
||||
|
||||
/**
|
||||
* The maximum size buffer to allocate.
|
||||
*
|
||||
* <p>This is set to the same size used in the JDK {@code java.util.ArrayList}:</p>
|
||||
* <blockquote>
|
||||
* Some VMs reserve some header words in an array.
|
||||
* Attempts to allocate larger arrays may result in
|
||||
* OutOfMemoryError: Requested array size exceeds VM limit.
|
||||
* </blockquote>
|
||||
*/
|
||||
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
/** Mask used to extract 8 bits, used in decoding bytes */
|
||||
protected static final int MASK_8BITS = 0xff;
|
||||
|
||||
/**
|
||||
* Byte used to pad output.
|
||||
*/
|
||||
protected static final byte PAD_DEFAULT = '='; // Allow static access to default
|
||||
|
||||
protected final byte pad; // instance variable just in case it needs to vary later
|
||||
|
||||
/** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */
|
||||
private final int unencodedBlockSize;
|
||||
|
||||
/** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */
|
||||
private final int encodedBlockSize;
|
||||
|
||||
/**
|
||||
* Chunksize for encoding. Not used when decoding.
|
||||
* A value of zero or less implies no chunking of the encoded data.
|
||||
* Rounded down to nearest multiple of encodedBlockSize.
|
||||
*/
|
||||
protected final int lineLength;
|
||||
|
||||
/**
|
||||
* Size of chunk separator. Not used unless {@link #lineLength} > 0.
|
||||
*/
|
||||
private final int chunkSeparatorLength;
|
||||
|
||||
/**
|
||||
* Note <code>lineLength</code> is rounded down to the nearest multiple of the encoded block size.
|
||||
* If <code>chunkSeparatorLength</code> is zero, then chunking is disabled.
|
||||
* @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
|
||||
* @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
|
||||
* @param lineLength if > 0, use chunking with a length <code>lineLength</code>
|
||||
* @param chunkSeparatorLength the chunk separator length, if relevant
|
||||
*/
|
||||
protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize,
|
||||
final int lineLength, final int chunkSeparatorLength) {
|
||||
this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, PAD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note <code>lineLength</code> is rounded down to the nearest multiple of the encoded block size.
|
||||
* If <code>chunkSeparatorLength</code> is zero, then chunking is disabled.
|
||||
* @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
|
||||
* @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
|
||||
* @param lineLength if > 0, use chunking with a length <code>lineLength</code>
|
||||
* @param chunkSeparatorLength the chunk separator length, if relevant
|
||||
* @param pad byte used as padding byte.
|
||||
*/
|
||||
protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize,
|
||||
final int lineLength, final int chunkSeparatorLength, final byte pad) {
|
||||
this.unencodedBlockSize = unencodedBlockSize;
|
||||
this.encodedBlockSize = encodedBlockSize;
|
||||
final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0;
|
||||
this.lineLength = useChunking ? (lineLength / encodedBlockSize) * encodedBlockSize : 0;
|
||||
this.chunkSeparatorLength = chunkSeparatorLength;
|
||||
|
||||
this.pad = pad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object has buffered data for reading.
|
||||
*
|
||||
* @param context the context to be used
|
||||
* @return true if there is data still available for reading.
|
||||
*/
|
||||
boolean hasData(final Context context) { // package protected for access from I/O streams
|
||||
return context.buffer != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of buffered data available for reading.
|
||||
*
|
||||
* @param context the context to be used
|
||||
* @return The amount of buffered data available for reading.
|
||||
*/
|
||||
int available(final Context context) { // package protected for access from I/O streams
|
||||
return context.buffer != null ? context.pos - context.readPos : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default buffer size. Can be overridden.
|
||||
*
|
||||
* @return the default buffer size.
|
||||
*/
|
||||
protected int getDefaultBufferSize() {
|
||||
return DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
|
||||
* @param context the context to be used
|
||||
* @param minCapacity the minimum required capacity
|
||||
* @return the resized byte[] buffer
|
||||
* @throws OutOfMemoryError if the {@code minCapacity} is negative
|
||||
*/
|
||||
private static byte[] resizeBuffer(final Context context, final int minCapacity) {
|
||||
// Overflow-conscious code treats the min and new capacity as unsigned.
|
||||
final int oldCapacity = context.buffer.length;
|
||||
int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;
|
||||
if (compareUnsigned(newCapacity, minCapacity) < 0) {
|
||||
newCapacity = minCapacity;
|
||||
}
|
||||
if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {
|
||||
newCapacity = createPositiveCapacity(minCapacity);
|
||||
}
|
||||
|
||||
final byte[] b = new byte[newCapacity];
|
||||
System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
|
||||
context.buffer = b;
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two {@code int} values numerically treating the values
|
||||
* as unsigned. Taken from JDK 1.8.
|
||||
*
|
||||
* <p>TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int).</p>
|
||||
*
|
||||
* @param x the first {@code int} to compare
|
||||
* @param y the second {@code int} to compare
|
||||
* @return the value {@code 0} if {@code x == y}; a value less
|
||||
* than {@code 0} if {@code x < y} as unsigned values; and
|
||||
* a value greater than {@code 0} if {@code x > y} as
|
||||
* unsigned values
|
||||
*/
|
||||
private static int compareUnsigned(int x, int y) {
|
||||
return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a positive capacity at least as large the minimum required capacity.
|
||||
* If the minimum capacity is negative then this throws an OutOfMemoryError as no array
|
||||
* can be allocated.
|
||||
*
|
||||
* @param minCapacity the minimum capacity
|
||||
* @return the capacity
|
||||
* @throws OutOfMemoryError if the {@code minCapacity} is negative
|
||||
*/
|
||||
private static int createPositiveCapacity(int minCapacity) {
|
||||
if (minCapacity < 0) {
|
||||
// overflow
|
||||
throw new OutOfMemoryError("Unable to allocate array size: " + (minCapacity & 0xffffffffL));
|
||||
}
|
||||
// This is called when we require buffer expansion to a very big array.
|
||||
// Use the conservative maximum buffer size if possible, otherwise the biggest required.
|
||||
//
|
||||
// Note: In this situation JDK 1.8 java.util.ArrayList returns Integer.MAX_VALUE.
|
||||
// This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a full
|
||||
// Integer.MAX_VALUE length array.
|
||||
// The result is that we may have to allocate an array of this size more than once if
|
||||
// the capacity must be expanded again.
|
||||
return (minCapacity > MAX_BUFFER_SIZE) ?
|
||||
minCapacity :
|
||||
MAX_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the buffer has room for <code>size</code> bytes
|
||||
*
|
||||
* @param size minimum spare space required
|
||||
* @param context the context to be used
|
||||
* @return the buffer
|
||||
*/
|
||||
protected byte[] ensureBufferSize(final int size, final Context context){
|
||||
if (context.buffer == null) {
|
||||
context.buffer = new byte[getDefaultBufferSize()];
|
||||
context.pos = 0;
|
||||
context.readPos = 0;
|
||||
|
||||
// Overflow-conscious:
|
||||
// x + y > z == x + y - z > 0
|
||||
} else if (context.pos + size - context.buffer.length > 0) {
|
||||
return resizeBuffer(context, context.pos + size);
|
||||
}
|
||||
return context.buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail
|
||||
* bytes. Returns how many bytes were actually extracted.
|
||||
* <p>
|
||||
* Package protected for access from I/O streams.
|
||||
*
|
||||
* @param b
|
||||
* byte[] array to extract the buffered data into.
|
||||
* @param bPos
|
||||
* position in byte[] array to start extraction at.
|
||||
* @param bAvail
|
||||
* amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).
|
||||
* @param context
|
||||
* the context to be used
|
||||
* @return The number of bytes successfully extracted into the provided byte[] array.
|
||||
*/
|
||||
int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) {
|
||||
if (context.buffer != null) {
|
||||
final int len = Math.min(available(context), bAvail);
|
||||
System.arraycopy(context.buffer, context.readPos, b, bPos, len);
|
||||
context.readPos += len;
|
||||
if (context.readPos >= context.pos) {
|
||||
context.buffer = null; // so hasData() will return false, and this method can return -1
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return context.eof ? EOF : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a byte value is whitespace or not.
|
||||
* Whitespace is taken to mean: space, tab, CR, LF
|
||||
* @param byteToCheck
|
||||
* the byte to check
|
||||
* @return true if byte is whitespace, false otherwise
|
||||
*/
|
||||
protected static boolean isWhiteSpace(final byte byteToCheck) {
|
||||
switch (byteToCheck) {
|
||||
case ' ' :
|
||||
case '\n' :
|
||||
case '\r' :
|
||||
case '\t' :
|
||||
return true;
|
||||
default :
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of
|
||||
* the Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].
|
||||
*
|
||||
* @param obj
|
||||
* Object to encode
|
||||
* @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied.
|
||||
* @throws EncoderException
|
||||
* if the parameter supplied is not of type byte[]
|
||||
* @deprecated This unused method will be removed in Tomcat 9
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object encode(final Object obj) throws EncoderException {
|
||||
if (!(obj instanceof byte[])) {
|
||||
throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]");
|
||||
}
|
||||
return encode((byte[]) obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet.
|
||||
* Uses UTF8 encoding.
|
||||
*
|
||||
* @param pArray
|
||||
* a byte array containing binary data
|
||||
* @return A String containing only Base-N character data
|
||||
*/
|
||||
public String encodeToString(final byte[] pArray) {
|
||||
return StringUtils.newStringUtf8(encode(pArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet.
|
||||
* Uses UTF8 encoding.
|
||||
*
|
||||
* @param pArray a byte array containing binary data
|
||||
* @return String containing only character data in the appropriate alphabet.
|
||||
* @since 1.5
|
||||
* This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring.
|
||||
*/
|
||||
public String encodeAsString(final byte[] pArray){
|
||||
return StringUtils.newStringUtf8(encode(pArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of
|
||||
* the Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String.
|
||||
*
|
||||
* @param obj
|
||||
* Object to decode
|
||||
* @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String
|
||||
* supplied.
|
||||
* @throws DecoderException
|
||||
* if the parameter supplied is not of type byte[]
|
||||
* @deprecated This unused method will be removed in Tomcat 9
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public Object decode(final Object obj) throws DecoderException {
|
||||
if (obj instanceof byte[]) {
|
||||
return decode((byte[]) obj);
|
||||
} else if (obj instanceof String) {
|
||||
return decode((String) obj);
|
||||
} else {
|
||||
throw new DecoderException("Parameter supplied to Base-N decode is not a byte[] or a String");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a String containing characters in the Base-N alphabet.
|
||||
*
|
||||
* @param pArray
|
||||
* A String containing Base-N character data
|
||||
* @return a byte array containing binary data
|
||||
*/
|
||||
public byte[] decode(final String pArray) {
|
||||
return decode(StringUtils.getBytesUtf8(pArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a byte[] containing characters in the Base-N alphabet.
|
||||
*
|
||||
* @param pArray
|
||||
* A byte array containing Base-N character data
|
||||
* @return a byte array containing binary data
|
||||
*/
|
||||
@Override
|
||||
public byte[] decode(final byte[] pArray) {
|
||||
return decode(pArray, 0, pArray.length);
|
||||
}
|
||||
|
||||
public byte[] decode(final byte[] pArray, final int off, final int len) {
|
||||
if (pArray == null || len == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
final Context context = new Context();
|
||||
decode(pArray, off, len, context);
|
||||
decode(pArray, off, EOF, context); // Notify decoder of EOF.
|
||||
final byte[] result = new byte[context.pos];
|
||||
readResults(result, 0, result.length, context);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet.
|
||||
*
|
||||
* @param pArray
|
||||
* a byte array containing binary data
|
||||
* @return A byte array containing only the base N alphabetic character data
|
||||
*/
|
||||
@Override
|
||||
public byte[] encode(final byte[] pArray) {
|
||||
if (pArray == null || pArray.length == 0) {
|
||||
return pArray;
|
||||
}
|
||||
return encode(pArray, 0, pArray.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte[] containing binary data, into a byte[] containing
|
||||
* characters in the alphabet.
|
||||
*
|
||||
* @param pArray
|
||||
* a byte array containing binary data
|
||||
* @param offset
|
||||
* initial offset of the subarray.
|
||||
* @param length
|
||||
* length of the subarray.
|
||||
* @return A byte array containing only the base N alphabetic character data
|
||||
* @since 1.11
|
||||
*/
|
||||
public byte[] encode(final byte[] pArray, final int offset, final int length) {
|
||||
if (pArray == null || pArray.length == 0) {
|
||||
return pArray;
|
||||
}
|
||||
final Context context = new Context();
|
||||
encode(pArray, offset, length, context);
|
||||
encode(pArray, offset, EOF, context); // Notify encoder of EOF.
|
||||
final byte[] buf = new byte[context.pos - context.readPos];
|
||||
readResults(buf, 0, buf.length, context);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// package protected for access from I/O streams
|
||||
abstract void encode(byte[] pArray, int i, int length, Context context);
|
||||
|
||||
// package protected for access from I/O streams
|
||||
abstract void decode(byte[] pArray, int i, int length, Context context);
|
||||
|
||||
/**
|
||||
* Returns whether or not the <code>octet</code> is in the current alphabet.
|
||||
* Does not allow whitespace or pad.
|
||||
*
|
||||
* @param value The value to test
|
||||
*
|
||||
* @return <code>true</code> if the value is defined in the current alphabet, <code>false</code> otherwise.
|
||||
*/
|
||||
protected abstract boolean isInAlphabet(byte value);
|
||||
|
||||
/**
|
||||
* Tests a given byte array to see if it contains only valid characters within the alphabet.
|
||||
* The method optionally treats whitespace and pad as valid.
|
||||
*
|
||||
* @param arrayOctet byte array to test
|
||||
* @param allowWSPad if <code>true</code>, then whitespace and PAD are also allowed
|
||||
*
|
||||
* @return <code>true</code> if all bytes are valid characters in the alphabet or if the byte array is empty;
|
||||
* <code>false</code>, otherwise
|
||||
*/
|
||||
public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) {
|
||||
for (final byte octet : arrayOctet) {
|
||||
if (!isInAlphabet(octet) &&
|
||||
(!allowWSPad || (octet != pad) && !isWhiteSpace(octet))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a given String to see if it contains only valid characters within the alphabet.
|
||||
* The method treats whitespace and PAD as valid.
|
||||
*
|
||||
* @param basen String to test
|
||||
* @return <code>true</code> if all characters in the String are valid characters in the alphabet or if
|
||||
* the String is empty; <code>false</code>, otherwise
|
||||
* @see #isInAlphabet(byte[], boolean)
|
||||
*/
|
||||
public boolean isInAlphabet(final String basen) {
|
||||
return isInAlphabet(StringUtils.getBytesUtf8(basen), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a given byte array to see if it contains any characters within the alphabet or PAD.
|
||||
*
|
||||
* Intended for use in checking line-ending arrays
|
||||
*
|
||||
* @param arrayOctet
|
||||
* byte array to test
|
||||
* @return <code>true</code> if any byte is a valid character in the alphabet or PAD; <code>false</code> otherwise
|
||||
*/
|
||||
protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
|
||||
if (arrayOctet == null) {
|
||||
return false;
|
||||
}
|
||||
for (final byte element : arrayOctet) {
|
||||
if (pad == element || isInAlphabet(element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the amount of space needed to encode the supplied array.
|
||||
*
|
||||
* @param pArray byte[] array which will later be encoded
|
||||
*
|
||||
* @return amount of space needed to encoded the supplied array.
|
||||
* Returns a long since a max-len array will require > Integer.MAX_VALUE
|
||||
*/
|
||||
public long getEncodedLength(final byte[] pArray) {
|
||||
// Calculate non-chunked size - rounded up to allow for padding
|
||||
// cast to long is needed to avoid possibility of overflow
|
||||
long len = ((pArray.length + unencodedBlockSize-1) / unencodedBlockSize) * (long) encodedBlockSize;
|
||||
if (lineLength > 0) { // We're using chunking
|
||||
// Round up to nearest multiple
|
||||
len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
base64.impossibleModulus=Impossible modulus [{0}]
|
||||
base64.inputTooLarge=Input array too large, the output array would be bigger [{0}] than the specified maximum size of [{1}]
|
||||
base64.lineSeparator=Line separator must not contain base64 characters [{0}]
|
||||
base64.nullEncodeParameter=Cannot encode integer with null parameter
|
||||
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
base64.impossibleModulus=Modulo [{0}] invalide
|
||||
base64.inputTooLarge=Le tableau en entrée est trop grand, la taille du tableau de sortie [{0}] serait plus grande que la taille maximale autorisée [{1}]
|
||||
base64.lineSeparator=Le séparateur de ligne ne doit pas contenir des caractères base64 [{0}]
|
||||
base64.nullEncodeParameter=Impossible d'encoder unentier en utilisant un paramètre null
|
||||
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
base64.impossibleModulus=計算できない剰余 [{0}] です。
|
||||
base64.inputTooLarge=入力配列が大きすぎます。出力配列は[{1}]の指定された最大サイズよりも大きくなります[{0}]。
|
||||
base64.lineSeparator=行区切り記号にはbase64文字を使用できません[{0}]
|
||||
base64.nullEncodeParameter=null 値を整数に符号化できませんでした。
|
||||
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
base64.impossibleModulus=불가능한 계수 [{0}]
|
||||
base64.inputTooLarge=입력 배열이 너무 큽니다. 출력 배열의 크기 [{0}]이(가), 지정된 최대 크기 [{1}] 보다 큰 값입니다.
|
||||
base64.lineSeparator=행 구분문자 [{0}]은(는) Base64 문자들을 포함해서는 안됩니다.
|
||||
base64.nullEncodeParameter=정수를 위한 파라미터가 널이라서 인코딩할 수 없습니다.
|
||||
100
java/org/apache/tomcat/util/codec/binary/StringUtils.java
Normal file
100
java/org/apache/tomcat/util/codec/binary/StringUtils.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.codec.binary;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Converts String to and from bytes using the encodings required by the Java specification. These encodings are
|
||||
* specified in <a href="http://download.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">
|
||||
* Standard charsets</a>.
|
||||
*
|
||||
* <p>This class is immutable and thread-safe.</p>
|
||||
*
|
||||
* @see <a href="http://download.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard charsets</a>
|
||||
* @since 1.4
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
/**
|
||||
* Calls {@link String#getBytes(Charset)}
|
||||
*
|
||||
* @param string
|
||||
* The string to encode (if null, return null).
|
||||
* @param charset
|
||||
* The {@link Charset} to encode the <code>String</code>
|
||||
* @return the encoded bytes
|
||||
*/
|
||||
private static byte[] getBytes(final String string, final Charset charset) {
|
||||
if (string == null) {
|
||||
return null;
|
||||
}
|
||||
return string.getBytes(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte
|
||||
* array.
|
||||
*
|
||||
* @param string
|
||||
* the String to encode, may be <code>null</code>
|
||||
* @return encoded bytes, or <code>null</code> if the input string was <code>null</code>
|
||||
* @see <a href="http://download.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard charsets</a>
|
||||
*/
|
||||
public static byte[] getBytesUtf8(final String string) {
|
||||
return getBytes(string, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>String</code> by decoding the specified array of bytes using the given charset.
|
||||
*
|
||||
* @param bytes
|
||||
* The bytes to be decoded into characters
|
||||
* @param charset
|
||||
* The {@link Charset} to encode the <code>String</code>
|
||||
* @return A new <code>String</code> decoded from the specified array of bytes using the given charset,
|
||||
* or <code>null</code> if the input byte array was <code>null</code>.
|
||||
*/
|
||||
private static String newString(final byte[] bytes, final Charset charset) {
|
||||
return bytes == null ? null : new String(bytes, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>String</code> by decoding the specified array of bytes using the US-ASCII charset.
|
||||
*
|
||||
* @param bytes
|
||||
* The bytes to be decoded into characters
|
||||
* @return A new <code>String</code> decoded from the specified array of bytes using the US-ASCII charset,
|
||||
* or <code>null</code> if the input byte array was <code>null</code>.
|
||||
*/
|
||||
public static String newStringUsAscii(final byte[] bytes) {
|
||||
return newString(bytes, StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>String</code> by decoding the specified array of bytes using the UTF-8 charset.
|
||||
*
|
||||
* @param bytes
|
||||
* The bytes to be decoded into characters
|
||||
* @return A new <code>String</code> decoded from the specified array of bytes using the UTF-8 charset,
|
||||
* or <code>null</code> if the input byte array was <code>null</code>.
|
||||
*/
|
||||
public static String newStringUtf8(final byte[] bytes) {
|
||||
return newString(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
21
java/org/apache/tomcat/util/codec/binary/package.html
Normal file
21
java/org/apache/tomcat/util/codec/binary/package.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
Base64, Base32, Binary, and Hexadecimal String encoding and decoding.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.collections;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* A Map implementation that uses case-insensitive (using {@link
|
||||
* Locale#ENGLISH}) strings as keys.
|
||||
* <p>
|
||||
* Keys must be instances of {@link String}. Note that this means that
|
||||
* <code>null</code> keys are not permitted.
|
||||
* <p>
|
||||
* This implementation is not thread-safe.
|
||||
*
|
||||
* @param <V> Type of values placed in this Map.
|
||||
*/
|
||||
public class CaseInsensitiveKeyMap<V> extends AbstractMap<String,V> {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(CaseInsensitiveKeyMap.class);
|
||||
|
||||
private final Map<Key,V> map = new HashMap<>();
|
||||
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return map.get(Key.getInstance(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public V put(String key, V value) {
|
||||
Key caseInsensitiveKey = Key.getInstance(key);
|
||||
if (caseInsensitiveKey == null) {
|
||||
throw new NullPointerException(sm.getString("caseInsensitiveKeyMap.nullKey"));
|
||||
}
|
||||
return map.put(caseInsensitiveKey, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* <b>Use this method with caution</b>. If the input Map contains duplicate
|
||||
* keys when the keys are compared in a case insensitive manner then some
|
||||
* values will be lost when inserting via this method.
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends V> m) {
|
||||
super.putAll(m);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return map.containsKey(Key.getInstance(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return map.remove(Key.getInstance(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, V>> entrySet() {
|
||||
return new EntrySet<>(map.entrySet());
|
||||
}
|
||||
|
||||
|
||||
private static class EntrySet<V> extends AbstractSet<Entry<String,V>> {
|
||||
|
||||
private final Set<Entry<Key,V>> entrySet;
|
||||
|
||||
public EntrySet(Set<Map.Entry<Key,V>> entrySet) {
|
||||
this.entrySet = entrySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<String,V>> iterator() {
|
||||
return new EntryIterator<>(entrySet.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return entrySet.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class EntryIterator<V> implements Iterator<Entry<String,V>> {
|
||||
|
||||
private final Iterator<Entry<Key,V>> iterator;
|
||||
|
||||
public EntryIterator(Iterator<Entry<Key,V>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String,V> next() {
|
||||
Entry<Key,V> entry = iterator.next();
|
||||
return new EntryImpl<>(entry.getKey().getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class EntryImpl<V> implements Entry<String,V> {
|
||||
|
||||
private final String key;
|
||||
private final V value;
|
||||
|
||||
public EntryImpl(String key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Key {
|
||||
|
||||
private final String key;
|
||||
private final String lcKey;
|
||||
|
||||
private Key(String key) {
|
||||
this.key = key;
|
||||
this.lcKey = key.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return lcKey.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Key other = (Key) obj;
|
||||
return lcKey.equals(other.lcKey);
|
||||
}
|
||||
|
||||
public static Key getInstance(Object o) {
|
||||
if (o instanceof String) {
|
||||
return new Key((String) o);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
java/org/apache/tomcat/util/collections/ConcurrentCache.java
Normal file
59
java/org/apache/tomcat/util/collections/ConcurrentCache.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.collections;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class ConcurrentCache<K,V> {
|
||||
|
||||
private final int size;
|
||||
|
||||
private final Map<K,V> eden;
|
||||
|
||||
private final Map<K,V> longterm;
|
||||
|
||||
public ConcurrentCache(int size) {
|
||||
this.size = size;
|
||||
this.eden = new ConcurrentHashMap<>(size);
|
||||
this.longterm = new WeakHashMap<>(size);
|
||||
}
|
||||
|
||||
public V get(K k) {
|
||||
V v = this.eden.get(k);
|
||||
if (v == null) {
|
||||
synchronized (longterm) {
|
||||
v = this.longterm.get(k);
|
||||
}
|
||||
if (v != null) {
|
||||
this.eden.put(k, v);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public void put(K k, V v) {
|
||||
if (this.eden.size() >= size) {
|
||||
synchronized (longterm) {
|
||||
this.longterm.putAll(this.eden);
|
||||
}
|
||||
this.eden.clear();
|
||||
}
|
||||
this.eden.put(k, v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.collections;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Concurrent hash map that holds its keys via weak references. Unlike
|
||||
* <code>WeakHashMap</code> this class does not handle dead keys during common
|
||||
* access operations, but expects you to call its {@link #maintain()} method
|
||||
* periodically. Both keys and values are expected to be not-<code>null</code>.
|
||||
*
|
||||
* @param <K> The type of keys used with the Map instance
|
||||
* @param <V> The type of values used with the Map instance
|
||||
*/
|
||||
public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implements
|
||||
ConcurrentMap<K, V> {
|
||||
|
||||
private final ConcurrentMap<Key, V> map = new ConcurrentHashMap<>();
|
||||
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
|
||||
/**
|
||||
* Method, that has to be invoked periodically to clean dead keys from the
|
||||
* map.
|
||||
*/
|
||||
public void maintain() {
|
||||
Key key;
|
||||
while ((key = (Key) queue.poll()) != null) {
|
||||
if (key.isDead()) {
|
||||
// No need to lookup if the key is not in the map
|
||||
continue;
|
||||
}
|
||||
key.ackDeath();
|
||||
map.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Key extends WeakReference<Object> {
|
||||
private final int hash;
|
||||
private boolean dead;
|
||||
|
||||
public Key(Object key, ReferenceQueue<Object> queue) {
|
||||
super(key, queue);
|
||||
hash = key.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (dead) {
|
||||
// Post-mortem cleanup looks for this specific Reference
|
||||
// instance
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof Reference<?>)) {
|
||||
return false;
|
||||
}
|
||||
Object oA = get();
|
||||
Object oB = ((Reference<?>) obj).get();
|
||||
if (oA == oB) {
|
||||
return true;
|
||||
}
|
||||
if (oA == null || oB == null) {
|
||||
return false;
|
||||
}
|
||||
return oA.equals(oB);
|
||||
}
|
||||
|
||||
public void ackDeath() {
|
||||
this.dead = true;
|
||||
}
|
||||
|
||||
public boolean isDead() {
|
||||
return dead;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Key instance to be used to store values in the map. It is
|
||||
* registered with the ReferenceQueue.
|
||||
*/
|
||||
private Key createStoreKey(Object key) {
|
||||
return new Key(key, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Key instance to be used only to lookup values in the map. It is
|
||||
* not registered with the ReferenceQueue.
|
||||
*/
|
||||
private Key createLookupKey(Object key) {
|
||||
return new Key(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
return map.containsKey(createLookupKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
return map.get(createLookupKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
Objects.requireNonNull(value);
|
||||
return map.put(createStoreKey(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return map.remove(createLookupKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
map.clear();
|
||||
// maintain() clears the queue, though it is not 100% reliable,
|
||||
// as queue is populated by GC asynchronously
|
||||
maintain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(K key, V value) {
|
||||
Objects.requireNonNull(value);
|
||||
Key storeKey = createStoreKey(key);
|
||||
V oldValue = map.putIfAbsent(storeKey, value);
|
||||
if (oldValue != null) { // ack that key has not been stored
|
||||
storeKey.ackDeath();
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return map.remove(createLookupKey(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
Objects.requireNonNull(newValue);
|
||||
return map.replace(createLookupKey(key), oldValue, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V replace(K key, V value) {
|
||||
Objects.requireNonNull(value);
|
||||
return map.replace(createLookupKey(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
return new AbstractSet<Map.Entry<K, V>>() {
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new Iterator<Map.Entry<K, V>>() {
|
||||
private final Iterator<Map.Entry<Key, V>> it = map
|
||||
.entrySet().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, V> next() {
|
||||
return new Map.Entry<K, V>() {
|
||||
private final Map.Entry<Key, V> en = it.next();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public K getKey() {
|
||||
return (K) en.getKey().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return en.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
Objects.requireNonNull(value);
|
||||
return en.setValue(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
it.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
105
java/org/apache/tomcat/util/collections/SynchronizedQueue.java
Normal file
105
java/org/apache/tomcat/util/collections/SynchronizedQueue.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.collections;
|
||||
|
||||
/**
|
||||
* This is intended as a (mostly) GC-free alternative to
|
||||
* {@link java.util.concurrent.ConcurrentLinkedQueue} when the requirement is to
|
||||
* create an unbounded queue with no requirement to shrink the queue. The aim is
|
||||
* to provide the bare minimum of required functionality as quickly as possible
|
||||
* with minimum garbage.
|
||||
*
|
||||
* @param <T> The type of object managed by this queue
|
||||
*/
|
||||
public class SynchronizedQueue<T> {
|
||||
|
||||
public static final int DEFAULT_SIZE = 128;
|
||||
|
||||
private Object[] queue;
|
||||
private int size;
|
||||
private int insert = 0;
|
||||
private int remove = 0;
|
||||
|
||||
public SynchronizedQueue() {
|
||||
this(DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
public SynchronizedQueue(int initialSize) {
|
||||
queue = new Object[initialSize];
|
||||
size = initialSize;
|
||||
}
|
||||
|
||||
public synchronized boolean offer(T t) {
|
||||
queue[insert++] = t;
|
||||
|
||||
// Wrap
|
||||
if (insert == size) {
|
||||
insert = 0;
|
||||
}
|
||||
|
||||
if (insert == remove) {
|
||||
expand();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized T poll() {
|
||||
if (insert == remove) {
|
||||
// empty
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) queue[remove];
|
||||
queue[remove] = null;
|
||||
remove++;
|
||||
|
||||
// Wrap
|
||||
if (remove == size) {
|
||||
remove = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void expand() {
|
||||
int newSize = size * 2;
|
||||
Object[] newQueue = new Object[newSize];
|
||||
|
||||
System.arraycopy(queue, insert, newQueue, 0, size - insert);
|
||||
System.arraycopy(queue, 0, newQueue, size - insert, insert);
|
||||
|
||||
insert = size;
|
||||
remove = 0;
|
||||
queue = newQueue;
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
public synchronized int size() {
|
||||
int result = insert - remove;
|
||||
if (result < 0) {
|
||||
result += size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
queue = new Object[size];
|
||||
insert = 0;
|
||||
remove = 0;
|
||||
}
|
||||
}
|
||||
105
java/org/apache/tomcat/util/collections/SynchronizedStack.java
Normal file
105
java/org/apache/tomcat/util/collections/SynchronizedStack.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.collections;
|
||||
|
||||
/**
|
||||
* This is intended as a (mostly) GC-free alternative to
|
||||
* {@link java.util.concurrent.ConcurrentLinkedQueue} when the requirement is to
|
||||
* create a pool of re-usable objects with no requirement to shrink the pool.
|
||||
* The aim is to provide the bare minimum of required functionality as quickly
|
||||
* as possible with minimum garbage.
|
||||
*
|
||||
* @param <T> The type of object managed by this stack
|
||||
*/
|
||||
public class SynchronizedStack<T> {
|
||||
|
||||
public static final int DEFAULT_SIZE = 128;
|
||||
private static final int DEFAULT_LIMIT = -1;
|
||||
|
||||
private int size;
|
||||
private final int limit;
|
||||
|
||||
/*
|
||||
* Points to the next available object in the stack
|
||||
*/
|
||||
private int index = -1;
|
||||
|
||||
private Object[] stack;
|
||||
|
||||
|
||||
public SynchronizedStack() {
|
||||
this(DEFAULT_SIZE, DEFAULT_LIMIT);
|
||||
}
|
||||
|
||||
public SynchronizedStack(int size, int limit) {
|
||||
if (limit > -1 && size > limit) {
|
||||
this.size = limit;
|
||||
} else {
|
||||
this.size = size;
|
||||
}
|
||||
this.limit = limit;
|
||||
stack = new Object[size];
|
||||
}
|
||||
|
||||
|
||||
public synchronized boolean push(T obj) {
|
||||
index++;
|
||||
if (index == size) {
|
||||
if (limit == -1 || size < limit) {
|
||||
expand();
|
||||
} else {
|
||||
index--;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stack[index] = obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized T pop() {
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
T result = (T) stack[index];
|
||||
stack[index--] = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
if (index > -1) {
|
||||
for (int i = 0; i < index + 1; i++) {
|
||||
stack[i] = null;
|
||||
}
|
||||
}
|
||||
index = -1;
|
||||
}
|
||||
|
||||
private void expand() {
|
||||
int newSize = size * 2;
|
||||
if (limit != -1 && newSize > limit) {
|
||||
newSize = limit;
|
||||
}
|
||||
Object[] newStack = new Object[newSize];
|
||||
System.arraycopy(stack, 0, newStack, 0, size);
|
||||
// This is the only point where garbage is created by throwing away the
|
||||
// old array. Note it is only the array, not the contents, that becomes
|
||||
// garbage.
|
||||
stack = newStack;
|
||||
size = newSize;
|
||||
}
|
||||
}
|
||||
114
java/org/apache/tomcat/util/compat/Jre8Compat.java
Normal file
114
java/org/apache/tomcat/util/compat/Jre8Compat.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.compat;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.security.KeyStore.LoadStoreParameter;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
class Jre8Compat extends JreCompat {
|
||||
|
||||
private static final Log log = LogFactory.getLog(Jre8Compat.class);
|
||||
private static final StringManager sm = StringManager.getManager(Jre8Compat.class);
|
||||
|
||||
private static final int RUNTIME_MAJOR_VERSION = 8;
|
||||
|
||||
private static final Method setUseCipherSuitesOrderMethod;
|
||||
private static final Constructor<?> domainLoadStoreParameterConstructor;
|
||||
|
||||
|
||||
static {
|
||||
Method m1 = null;
|
||||
Constructor<?> c2 = null;
|
||||
try {
|
||||
// Order is important for the error handling below.
|
||||
// Must look up m1 first.
|
||||
|
||||
// The class is Java6+...
|
||||
Class<?> clazz1 = Class.forName("javax.net.ssl.SSLParameters");
|
||||
// ...but this method is Java8+
|
||||
m1 = clazz1.getMethod("setUseCipherSuitesOrder", boolean.class);
|
||||
Class<?> clazz2 = Class.forName("java.security.DomainLoadStoreParameter");
|
||||
c2 = clazz2.getConstructor(URI.class, Map.class);
|
||||
} catch (SecurityException e) {
|
||||
// Should never happen
|
||||
log.error(sm.getString("jre8Compat.unexpected"), e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (m1 == null) {
|
||||
// Must be pre-Java 8
|
||||
log.debug(sm.getString("jre8Compat.javaPre8"), e);
|
||||
} else {
|
||||
// Should never happen - signature error in lookup?
|
||||
log.error(sm.getString("jre8Compat.unexpected"), e);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Should never happen
|
||||
log.error(sm.getString("jre8Compat.unexpected"), e);
|
||||
}
|
||||
setUseCipherSuitesOrderMethod = m1;
|
||||
domainLoadStoreParameterConstructor = c2;
|
||||
}
|
||||
|
||||
|
||||
static boolean isSupported() {
|
||||
return setUseCipherSuitesOrderMethod != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setUseServerCipherSuitesOrder(SSLParameters sslParameters,
|
||||
boolean useCipherSuitesOrder) {
|
||||
try {
|
||||
setUseCipherSuitesOrderMethod.invoke(sslParameters,
|
||||
Boolean.valueOf(useCipherSuitesOrder));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LoadStoreParameter getDomainLoadStoreParameter(URI uri) {
|
||||
try {
|
||||
return (LoadStoreParameter) domainLoadStoreParameterConstructor.newInstance(
|
||||
uri, Collections.EMPTY_MAP);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
|
||||
InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int jarFileRuntimeMajorVersion() {
|
||||
return RUNTIME_MAJOR_VERSION;
|
||||
}
|
||||
}
|
||||
277
java/org/apache/tomcat/util/compat/Jre9Compat.java
Normal file
277
java/org/apache/tomcat/util/compat/Jre9Compat.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.compat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Deque;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
class Jre9Compat extends Jre8Compat {
|
||||
|
||||
private static final Log log = LogFactory.getLog(Jre9Compat.class);
|
||||
private static final StringManager sm = StringManager.getManager(Jre9Compat.class);
|
||||
|
||||
private static final Class<?> inaccessibleObjectExceptionClazz;
|
||||
private static final Method setApplicationProtocolsMethod;
|
||||
private static final Method getApplicationProtocolMethod;
|
||||
private static final Method setDefaultUseCachesMethod;
|
||||
private static final Method bootMethod;
|
||||
private static final Method configurationMethod;
|
||||
private static final Method modulesMethod;
|
||||
private static final Method referenceMethod;
|
||||
private static final Method locationMethod;
|
||||
private static final Method isPresentMethod;
|
||||
private static final Method getMethod;
|
||||
private static final Constructor<JarFile> jarFileConstructor;
|
||||
private static final Method isMultiReleaseMethod;
|
||||
private static final Object RUNTIME_VERSION;
|
||||
private static final int RUNTIME_MAJOR_VERSION;
|
||||
private static final Method canAccessMethod;
|
||||
private static final Method getModuleMethod;
|
||||
private static final Method isExportedMethod;
|
||||
|
||||
static {
|
||||
Class<?> c1 = null;
|
||||
Method m2 = null;
|
||||
Method m3 = null;
|
||||
Method m4 = null;
|
||||
Method m5 = null;
|
||||
Method m6 = null;
|
||||
Method m7 = null;
|
||||
Method m8 = null;
|
||||
Method m9 = null;
|
||||
Method m10 = null;
|
||||
Method m11 = null;
|
||||
Constructor<JarFile> c12 = null;
|
||||
Method m13 = null;
|
||||
Object o14 = null;
|
||||
Object o15 = null;
|
||||
Method m16 = null;
|
||||
Method m17 = null;
|
||||
Method m18 = null;
|
||||
|
||||
try {
|
||||
// Order is important for the error handling below.
|
||||
// Must look up c1 first.
|
||||
c1 = Class.forName("java.lang.reflect.InaccessibleObjectException");
|
||||
|
||||
Class<?> moduleLayerClazz = Class.forName("java.lang.ModuleLayer");
|
||||
Class<?> configurationClazz = Class.forName("java.lang.module.Configuration");
|
||||
Class<?> resolvedModuleClazz = Class.forName("java.lang.module.ResolvedModule");
|
||||
Class<?> moduleReferenceClazz = Class.forName("java.lang.module.ModuleReference");
|
||||
Class<?> optionalClazz = Class.forName("java.util.Optional");
|
||||
Class<?> versionClazz = Class.forName("java.lang.Runtime$Version");
|
||||
Method runtimeVersionMethod = JarFile.class.getMethod("runtimeVersion");
|
||||
Method majorMethod = versionClazz.getMethod("major");
|
||||
|
||||
m2 = SSLParameters.class.getMethod("setApplicationProtocols", String[].class);
|
||||
m3 = SSLEngine.class.getMethod("getApplicationProtocol");
|
||||
m4 = URLConnection.class.getMethod("setDefaultUseCaches", String.class, boolean.class);
|
||||
m5 = moduleLayerClazz.getMethod("boot");
|
||||
m6 = moduleLayerClazz.getMethod("configuration");
|
||||
m7 = configurationClazz.getMethod("modules");
|
||||
m8 = resolvedModuleClazz.getMethod("reference");
|
||||
m9 = moduleReferenceClazz.getMethod("location");
|
||||
m10 = optionalClazz.getMethod("isPresent");
|
||||
m11 = optionalClazz.getMethod("get");
|
||||
c12 = JarFile.class.getConstructor(File.class, boolean.class, int.class, versionClazz);
|
||||
m13 = JarFile.class.getMethod("isMultiRelease");
|
||||
o14 = runtimeVersionMethod.invoke(null);
|
||||
o15 = majorMethod.invoke(o14);
|
||||
m16 = AccessibleObject.class.getMethod("canAccess", new Class<?>[] { Object.class });
|
||||
m17 = Class.class.getMethod("getModule");
|
||||
Class<?> moduleClass = Class.forName("java.lang.Module");
|
||||
m18 = moduleClass.getMethod("isExported", String.class);
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (c1 == null) {
|
||||
// Must be pre-Java 9
|
||||
log.debug(sm.getString("jre9Compat.javaPre9"), e);
|
||||
} else {
|
||||
// Should never happen - signature error in lookup?
|
||||
log.error(sm.getString("jre9Compat.unexpected"), e);
|
||||
}
|
||||
} catch (ReflectiveOperationException | IllegalArgumentException e) {
|
||||
// Should never happen
|
||||
log.error(sm.getString("jre9Compat.unexpected"), e);
|
||||
}
|
||||
|
||||
inaccessibleObjectExceptionClazz = c1;
|
||||
setApplicationProtocolsMethod = m2;
|
||||
getApplicationProtocolMethod = m3;
|
||||
setDefaultUseCachesMethod = m4;
|
||||
bootMethod = m5;
|
||||
configurationMethod = m6;
|
||||
modulesMethod = m7;
|
||||
referenceMethod = m8;
|
||||
locationMethod = m9;
|
||||
isPresentMethod = m10;
|
||||
getMethod = m11;
|
||||
jarFileConstructor = c12;
|
||||
isMultiReleaseMethod = m13;
|
||||
|
||||
RUNTIME_VERSION = o14;
|
||||
if (o15 != null) {
|
||||
RUNTIME_MAJOR_VERSION = ((Integer) o15).intValue();
|
||||
} else {
|
||||
// Must be Java 8
|
||||
RUNTIME_MAJOR_VERSION = 8;
|
||||
}
|
||||
|
||||
canAccessMethod = m16;
|
||||
getModuleMethod = m17;
|
||||
isExportedMethod = m18;
|
||||
}
|
||||
|
||||
|
||||
static boolean isSupported() {
|
||||
return inaccessibleObjectExceptionClazz != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isInstanceOfInaccessibleObjectException(Throwable t) {
|
||||
if (t == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return inaccessibleObjectExceptionClazz.isAssignableFrom(t.getClass());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setApplicationProtocols(SSLParameters sslParameters, String[] protocols) {
|
||||
try {
|
||||
setApplicationProtocolsMethod.invoke(sslParameters, (Object) protocols);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getApplicationProtocol(SSLEngine sslEngine) {
|
||||
try {
|
||||
return (String) getApplicationProtocolMethod.invoke(sslEngine);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void disableCachingForJarUrlConnections() throws IOException {
|
||||
try {
|
||||
setDefaultUseCachesMethod.invoke(null, "JAR", Boolean.FALSE);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addBootModulePath(Deque<URL> classPathUrlsToProcess) {
|
||||
try {
|
||||
Object bootLayer = bootMethod.invoke(null);
|
||||
Object bootConfiguration = configurationMethod.invoke(bootLayer);
|
||||
Set<?> resolvedModules = (Set<?>) modulesMethod.invoke(bootConfiguration);
|
||||
for (Object resolvedModule : resolvedModules) {
|
||||
Object moduleReference = referenceMethod.invoke(resolvedModule);
|
||||
Object optionalURI = locationMethod.invoke(moduleReference);
|
||||
Boolean isPresent = (Boolean) isPresentMethod.invoke(optionalURI);
|
||||
if (isPresent.booleanValue()) {
|
||||
URI uri = (URI) getMethod.invoke(optionalURI);
|
||||
try {
|
||||
URL url = uri.toURL();
|
||||
classPathUrlsToProcess.add(url);
|
||||
} catch (MalformedURLException e) {
|
||||
log.warn(sm.getString("jre9Compat.invalidModuleUri", uri), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JarFile jarFileNewInstance(File f) throws IOException {
|
||||
try {
|
||||
return jarFileConstructor.newInstance(
|
||||
f, Boolean.TRUE, Integer.valueOf(ZipFile.OPEN_READ), RUNTIME_VERSION);
|
||||
} catch (ReflectiveOperationException | IllegalArgumentException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean jarFileIsMultiRelease(JarFile jarFile) {
|
||||
try {
|
||||
return ((Boolean) isMultiReleaseMethod.invoke(jarFile)).booleanValue();
|
||||
} catch (ReflectiveOperationException | IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int jarFileRuntimeMajorVersion() {
|
||||
return RUNTIME_MAJOR_VERSION;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canAcccess(Object base, AccessibleObject accessibleObject) {
|
||||
try {
|
||||
return ((Boolean) canAccessMethod.invoke(accessibleObject, base)).booleanValue();
|
||||
} catch (ReflectiveOperationException | IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isExported(Class<?> type) {
|
||||
try {
|
||||
String packageName = type.getPackage().getName();
|
||||
Object module = getModuleMethod.invoke(type);
|
||||
return ((Boolean) isExportedMethod.invoke(module, packageName)).booleanValue();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
247
java/org/apache/tomcat/util/compat/JreCompat.java
Normal file
247
java/org/apache/tomcat/util/compat/JreCompat.java
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.compat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.KeyStore.LoadStoreParameter;
|
||||
import java.util.Deque;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* This is the base implementation class for JRE compatibility and provides an
|
||||
* implementation based on Java 7. Sub-classes may extend this class and provide
|
||||
* alternative implementations for later JRE versions
|
||||
*/
|
||||
public class JreCompat {
|
||||
|
||||
private static final int RUNTIME_MAJOR_VERSION = 7;
|
||||
|
||||
private static final JreCompat instance;
|
||||
private static StringManager sm =
|
||||
StringManager.getManager(JreCompat.class.getPackage().getName());
|
||||
private static final boolean jre9Available;
|
||||
private static final boolean jre8Available;
|
||||
|
||||
|
||||
static {
|
||||
// This is Tomcat 8 with a minimum Java version of Java 7. The latest
|
||||
// Java version the optional features require is Java 9.
|
||||
// Look for the highest supported JVM first
|
||||
if (Jre9Compat.isSupported()) {
|
||||
instance = new Jre9Compat();
|
||||
jre9Available = true;
|
||||
jre8Available = true;
|
||||
}
|
||||
else if (Jre8Compat.isSupported()) {
|
||||
instance = new Jre8Compat();
|
||||
jre9Available = false;
|
||||
jre8Available = true;
|
||||
} else {
|
||||
instance = new JreCompat();
|
||||
jre9Available = false;
|
||||
jre8Available = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static JreCompat getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
// Java 7 implementation of Java 8 methods
|
||||
|
||||
public static boolean isJre8Available() {
|
||||
return jre8Available;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setUseServerCipherSuitesOrder(SSLParameters engine, boolean useCipherSuitesOrder) {
|
||||
throw new UnsupportedOperationException(sm.getString("jreCompat.noServerCipherSuiteOrder"));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public LoadStoreParameter getDomainLoadStoreParameter(URI uri) {
|
||||
throw new UnsupportedOperationException(sm.getString("jreCompat.noDomainLoadStoreParameter"));
|
||||
}
|
||||
|
||||
|
||||
// Java 7 implementation of Java 9 methods
|
||||
|
||||
public static boolean isJre9Available() {
|
||||
return jre9Available;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if the provided exception is an instance of
|
||||
* java.lang.reflect.InaccessibleObjectException.
|
||||
*
|
||||
* @param t The exception to test
|
||||
*
|
||||
* @return {@code true} if the exception is an instance of
|
||||
* InaccessibleObjectException, otherwise {@code false}
|
||||
*/
|
||||
public boolean isInstanceOfInaccessibleObjectException(Throwable t) {
|
||||
// Exception does not exist prior to Java 9
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the application protocols the server will accept for ALPN
|
||||
*
|
||||
* @param sslParameters The SSL parameters for a connection
|
||||
* @param protocols The application protocols to be allowed for that
|
||||
* connection
|
||||
*/
|
||||
public void setApplicationProtocols(SSLParameters sslParameters, String[] protocols) {
|
||||
throw new UnsupportedOperationException(sm.getString("jreCompat.noApplicationProtocols"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the application protocol that has been negotiated for connection
|
||||
* associated with the given SSLEngine.
|
||||
*
|
||||
* @param sslEngine The SSLEngine for which to obtain the negotiated
|
||||
* protocol
|
||||
*
|
||||
* @return The name of the negotiated protocol
|
||||
*/
|
||||
public String getApplicationProtocol(SSLEngine sslEngine) {
|
||||
throw new UnsupportedOperationException(sm.getString("jreCompat.noApplicationProtocol"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disables caching for JAR URL connections. For Java 8 and earlier, this also disables
|
||||
* caching for ALL URL connections.
|
||||
*
|
||||
* @throws IOException If a dummy JAR URLConnection can not be created
|
||||
*/
|
||||
public void disableCachingForJarUrlConnections() throws IOException {
|
||||
// Doesn't matter that this JAR doesn't exist - just as
|
||||
// long as the URL is well-formed
|
||||
URL url = new URL("jar:file://dummy.jar!/");
|
||||
URLConnection uConn = url.openConnection();
|
||||
uConn.setDefaultUseCaches(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the URLs for all the JARs on the module path when the JVM starts
|
||||
* and adds them to the provided Deque.
|
||||
*
|
||||
* @param classPathUrlsToProcess The Deque to which the modules should be
|
||||
* added
|
||||
*/
|
||||
public void addBootModulePath(Deque<URL> classPathUrlsToProcess) {
|
||||
// NO-OP for Java 7. There is no module path.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JarFile instance. When running on Java 9 and later, the
|
||||
* JarFile will be multi-release JAR aware. While this isn't strictly
|
||||
* required to be in this package, it is provided as a convenience method.
|
||||
*
|
||||
* @param s The JAR file to open
|
||||
*
|
||||
* @return A JarFile instance based on the provided path
|
||||
*
|
||||
* @throws IOException If an I/O error occurs creating the JarFile instance
|
||||
*/
|
||||
public final JarFile jarFileNewInstance(String s) throws IOException {
|
||||
return jarFileNewInstance(new File(s));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JarFile instance. When running on Java 9 and later, the
|
||||
* JarFile will be multi-release JAR aware.
|
||||
*
|
||||
* @param f The JAR file to open
|
||||
*
|
||||
* @return A JarFile instance based on the provided file
|
||||
*
|
||||
* @throws IOException If an I/O error occurs creating the JarFile instance
|
||||
*/
|
||||
public JarFile jarFileNewInstance(File f) throws IOException {
|
||||
return new JarFile(f);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this JarFile a multi-release JAR file.
|
||||
*
|
||||
* @param jarFile The JarFile to test
|
||||
*
|
||||
* @return {@code true} If it is a multi-release JAR file and is configured
|
||||
* to behave as such.
|
||||
*/
|
||||
public boolean jarFileIsMultiRelease(JarFile jarFile) {
|
||||
// Java 8 doesn't support multi-release so default to false
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public int jarFileRuntimeMajorVersion() {
|
||||
return RUNTIME_MAJOR_VERSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the accessibleObject accessible (as a result of appropriate module
|
||||
* exports) on the provided instance?
|
||||
*
|
||||
* @param base The specific instance to be tested.
|
||||
* @param accessibleObject The method/field/constructor to be tested.
|
||||
*
|
||||
* @return {code true} if the AccessibleObject can be accessed otherwise
|
||||
* {code false}
|
||||
*/
|
||||
public boolean canAcccess(Object base, AccessibleObject accessibleObject) {
|
||||
// Java 8 doesn't support modules so default to true
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the given class in an exported package?
|
||||
*
|
||||
* @param type The class to test
|
||||
*
|
||||
* @return Always {@code true} for Java 8. {@code true} if the enclosing
|
||||
* package is exported for Java 9+
|
||||
*/
|
||||
public boolean isExported(Class<?> type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
59
java/org/apache/tomcat/util/compat/JrePlatform.java
Normal file
59
java/org/apache/tomcat/util/compat/JrePlatform.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.compat;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
public class JrePlatform {
|
||||
|
||||
private static final String OS_NAME_PROPERTY = "os.name";
|
||||
private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
|
||||
|
||||
static {
|
||||
/*
|
||||
* There are a few places where a) the behaviour of the Java API depends
|
||||
* on the underlying platform and b) those behavioural differences have
|
||||
* an impact on Tomcat.
|
||||
*
|
||||
* Tomcat therefore needs to be able to determine the platform it is
|
||||
* running on to account for those differences.
|
||||
*
|
||||
* In an ideal world this code would not exist.
|
||||
*/
|
||||
|
||||
// This check is derived from the check in Apache Commons Lang
|
||||
String osName;
|
||||
if (System.getSecurityManager() == null) {
|
||||
osName = System.getProperty(OS_NAME_PROPERTY);
|
||||
} else {
|
||||
osName = AccessController.doPrivileged(
|
||||
new PrivilegedAction<String>() {
|
||||
|
||||
@Override
|
||||
public String run() {
|
||||
return System.getProperty(OS_NAME_PROPERTY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX);
|
||||
}
|
||||
|
||||
|
||||
public static final boolean IS_WINDOWS;
|
||||
}
|
||||
49
java/org/apache/tomcat/util/compat/JreVendor.java
Normal file
49
java/org/apache/tomcat/util/compat/JreVendor.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.compat;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class JreVendor {
|
||||
|
||||
static {
|
||||
/**
|
||||
* There are a few places where Tomcat either accesses JVM internals
|
||||
* (e.g. the memory leak protection) or where feature support varies
|
||||
* between JVMs (e.g. SPNEGO). These flags exist to enable Tomcat to
|
||||
* adjust its behaviour based on the vendor of the JVM. In an ideal
|
||||
* world this code would not exist.
|
||||
*/
|
||||
String vendor = System.getProperty("java.vendor", "");
|
||||
vendor = vendor.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
if (vendor.startsWith("oracle") || vendor.startsWith("sun")) {
|
||||
IS_ORACLE_JVM = true;
|
||||
IS_IBM_JVM = false;
|
||||
} else if (vendor.contains("ibm")) {
|
||||
IS_ORACLE_JVM = false;
|
||||
IS_IBM_JVM = true;
|
||||
} else {
|
||||
IS_ORACLE_JVM = false;
|
||||
IS_IBM_JVM = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean IS_ORACLE_JVM;
|
||||
|
||||
public static final boolean IS_IBM_JVM;
|
||||
}
|
||||
26
java/org/apache/tomcat/util/compat/LocalStrings.properties
Normal file
26
java/org/apache/tomcat/util/compat/LocalStrings.properties
Normal file
@@ -0,0 +1,26 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jre8Compat.javaPre8=Class not found so assuming code is running on a pre-Java 8 JVM
|
||||
jre8Compat.unexpected=Failed to create references to Java 8 classes and methods
|
||||
|
||||
jre9Compat.invalidModuleUri=The module URI provided [{0}] could not be converted to a URL for the JarScanner to process
|
||||
jre9Compat.javaPre9=Class not found so assuming code is running on a pre-Java 9 JVM
|
||||
jre9Compat.unexpected=Failed to create references to Java 9 classes and methods
|
||||
|
||||
jreCompat.noApplicationProtocol=Java Runtime does not support SSLEngine.getApplicationProtocol(). You must use Java 9 to use this feature.
|
||||
jreCompat.noApplicationProtocols=Java Runtime does not support SSLParameters.setApplicationProtocols(). You must use Java 9 to use this feature.
|
||||
jreCompat.noDomainLoadStoreParameter=Java Runtime does not support DKS key store type. You must use Java 8 or later to use this feature.
|
||||
jreCompat.noServerCipherSuiteOrder=Java Runtime does not support "useServerCipherSuitesOrder". You must use Java 8 or later to use this feature.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jreCompat.noApplicationProtocol=Die Java Runtime unterstützt nicht den SSLEngine.getApplicationProtocol() Aufruf. Um dieses Feature nutzen zu können muss Java 9 verwendet werden.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jre9Compat.invalidModuleUri=El módulo URI proveído [{0}] no pudo ser convertiod a una URL para ser procesado por JarScanner
|
||||
|
||||
jreCompat.noApplicationProtocol=Java Runtime no soporta SSLEngine.getApplicationProtocol(). Se necesita Java 9 para usar esta función.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jre9Compat.invalidModuleUri=L''URI du module fournie [{0}] n''a pas pu être convertie en URL pour être traitée par le JarScanner
|
||||
jre9Compat.javaPre9=Le code est considéré être exécuté sur une JVM antérieure à Java 9 car la classe n'a pas été trouvée
|
||||
jre9Compat.unexpected=Impossible de créer les références vers les classes et méthodes de Java 9
|
||||
|
||||
jreCompat.noApplicationProtocol=Le Java Runtime utilisé ne supporte pas SSLEngine.getApplicationProtocol(). Il faut Java 9 pour utiliser cette option.
|
||||
jreCompat.noApplicationProtocols=L'environnement Java ne supporte pas SSLParameters.setApplicationProtocols(), cette fonctionnalité demande Java 9
|
||||
@@ -0,0 +1,21 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jre9Compat.invalidModuleUri=モジュール URI [{0}] を JarScanner で処理する URL に変換できませんでした。
|
||||
|
||||
jreCompat.noApplicationProtocol=Java 実行環境が SSLEngine.getApplicationProtocol() に対応していません。Java 9 以降で実行する必要があります。
|
||||
jreCompat.noApplicationProtocols=\n\
|
||||
113/5000\n\
|
||||
Java RuntimeはSSLParameters.setApplicationProtocols()をサポートしていません。 この機能を使用するには、Java 9を使用する必要があります。
|
||||
@@ -0,0 +1,21 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jre9Compat.invalidModuleUri=[{0}](으)로 제공된 모듈 URI는 JarScanner가 처리할 수 있는 URL로 변환될 수 없습니다.
|
||||
jre9Compat.javaPre9=Java 9 클래스가 발견되지 않습니다. Java 9 이전 버전에서 동작하고 있는 것으로 보입니다.
|
||||
jre9Compat.unexpected=Java 9 클래스들과 메소드들을 참조할 수 없습니다.
|
||||
|
||||
jreCompat.noApplicationProtocol=자바 런타임이 SSLEngine.getApplicationProtocol()을 지원하지 않습니다. 이 기능을 사용하려면 Java 9를 사용해야 합니다.
|
||||
jreCompat.noApplicationProtocols=자바 런타임이 SSLParameters.setApplicationProtocols()을 지원하지 않습니다. 이 기능을 사용하려면 Java 9를 사용해야 합니다.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
jre9Compat.invalidModuleUri=提供的模块URI [{0}]无法转换为JarScanner要处理的URL
|
||||
jre9Compat.javaPre9=类未找到,所以假设代码运行在pre-Java 8虚拟机上
|
||||
jre9Compat.unexpected=创建对Java 9类的依赖和方法失败
|
||||
|
||||
jreCompat.noApplicationProtocol=Java 运行时不支持 SSLEngine.getApplicationProtocol()。要使用该功能你必须使用 Java 9。
|
||||
49
java/org/apache/tomcat/util/compat/TLS.java
Normal file
49
java/org/apache/tomcat/util/compat/TLS.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.compat;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.tomcat.util.net.Constants;
|
||||
|
||||
/**
|
||||
* This class checks for the availability of TLS features.
|
||||
*
|
||||
* @deprecated Unused. This will be removed in Tomcat 10.
|
||||
*/
|
||||
@Deprecated
|
||||
public class TLS {
|
||||
|
||||
private static final boolean tlsv13Available;
|
||||
|
||||
static {
|
||||
boolean ok = false;
|
||||
try {
|
||||
SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3);
|
||||
ok = true;
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
}
|
||||
tlsv13Available = ok;
|
||||
}
|
||||
|
||||
public static boolean isTlsv13Available() {
|
||||
return tlsv13Available;
|
||||
}
|
||||
|
||||
}
|
||||
25
java/org/apache/tomcat/util/descriptor/Constants.java
Normal file
25
java/org/apache/tomcat/util/descriptor/Constants.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.descriptor;
|
||||
|
||||
public class Constants {
|
||||
|
||||
public static final String PACKAGE_NAME =
|
||||
Constants.class.getPackage().getName();
|
||||
|
||||
public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null);
|
||||
}
|
||||
186
java/org/apache/tomcat/util/descriptor/DigesterFactory.java
Normal file
186
java/org/apache/tomcat/util/descriptor/DigesterFactory.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.descriptor;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.digester.Digester;
|
||||
import org.apache.tomcat.util.digester.RuleSet;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
import org.xml.sax.ext.EntityResolver2;
|
||||
|
||||
/**
|
||||
* Wrapper class around the Digester that hide Digester's initialization
|
||||
* details.
|
||||
*/
|
||||
public class DigesterFactory {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(Constants.PACKAGE_NAME);
|
||||
|
||||
private static final Class<ServletContext> CLASS_SERVLET_CONTEXT;
|
||||
private static final Class<?> CLASS_JSP_CONTEXT;
|
||||
|
||||
static {
|
||||
CLASS_SERVLET_CONTEXT = ServletContext.class;
|
||||
Class<?> jspContext = null;
|
||||
try {
|
||||
jspContext = Class.forName("javax.servlet.jsp.JspContext");
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Ignore - JSP API is not present.
|
||||
}
|
||||
CLASS_JSP_CONTEXT = jspContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mapping of well-known public IDs used by the Servlet API to the matching
|
||||
* local resource.
|
||||
*/
|
||||
public static final Map<String,String> SERVLET_API_PUBLIC_IDS;
|
||||
|
||||
/**
|
||||
* Mapping of well-known system IDs used by the Servlet API to the matching
|
||||
* local resource.
|
||||
*/
|
||||
public static final Map<String,String> SERVLET_API_SYSTEM_IDS;
|
||||
|
||||
static {
|
||||
Map<String, String> publicIds = new HashMap<>();
|
||||
Map<String, String> systemIds = new HashMap<>();
|
||||
|
||||
// W3C
|
||||
add(publicIds, XmlIdentifiers.XSD_10_PUBLIC, locationFor("XMLSchema.dtd"));
|
||||
add(publicIds, XmlIdentifiers.DATATYPES_PUBLIC, locationFor("datatypes.dtd"));
|
||||
add(systemIds, XmlIdentifiers.XML_2001_XSD, locationFor("xml.xsd"));
|
||||
|
||||
// from J2EE 1.2
|
||||
add(publicIds, XmlIdentifiers.WEB_22_PUBLIC, locationFor("web-app_2_2.dtd"));
|
||||
add(publicIds, XmlIdentifiers.TLD_11_PUBLIC, locationFor("web-jsptaglibrary_1_1.dtd"));
|
||||
|
||||
// from J2EE 1.3
|
||||
add(publicIds, XmlIdentifiers.WEB_23_PUBLIC, locationFor("web-app_2_3.dtd"));
|
||||
add(publicIds, XmlIdentifiers.TLD_12_PUBLIC, locationFor("web-jsptaglibrary_1_2.dtd"));
|
||||
|
||||
// from J2EE 1.4
|
||||
add(systemIds, "http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd",
|
||||
locationFor("j2ee_web_services_1_1.xsd"));
|
||||
add(systemIds, "http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd",
|
||||
locationFor("j2ee_web_services_client_1_1.xsd"));
|
||||
add(systemIds, XmlIdentifiers.WEB_24_XSD, locationFor("web-app_2_4.xsd"));
|
||||
add(systemIds, XmlIdentifiers.TLD_20_XSD, locationFor("web-jsptaglibrary_2_0.xsd"));
|
||||
addSelf(systemIds, "j2ee_1_4.xsd");
|
||||
addSelf(systemIds, "jsp_2_0.xsd");
|
||||
|
||||
// from JavaEE 5
|
||||
add(systemIds, XmlIdentifiers.WEB_25_XSD, locationFor("web-app_2_5.xsd"));
|
||||
add(systemIds, XmlIdentifiers.TLD_21_XSD, locationFor("web-jsptaglibrary_2_1.xsd"));
|
||||
addSelf(systemIds, "javaee_5.xsd");
|
||||
addSelf(systemIds, "jsp_2_1.xsd");
|
||||
addSelf(systemIds, "javaee_web_services_1_2.xsd");
|
||||
addSelf(systemIds, "javaee_web_services_client_1_2.xsd");
|
||||
|
||||
// from JavaEE 6
|
||||
add(systemIds, XmlIdentifiers.WEB_30_XSD, locationFor("web-app_3_0.xsd"));
|
||||
add(systemIds, XmlIdentifiers.WEB_FRAGMENT_30_XSD, locationFor("web-fragment_3_0.xsd"));
|
||||
addSelf(systemIds, "web-common_3_0.xsd");
|
||||
addSelf(systemIds, "javaee_6.xsd");
|
||||
addSelf(systemIds, "jsp_2_2.xsd");
|
||||
addSelf(systemIds, "javaee_web_services_1_3.xsd");
|
||||
addSelf(systemIds, "javaee_web_services_client_1_3.xsd");
|
||||
|
||||
// from JavaEE 7
|
||||
add(systemIds, XmlIdentifiers.WEB_31_XSD, locationFor("web-app_3_1.xsd"));
|
||||
add(systemIds, XmlIdentifiers.WEB_FRAGMENT_31_XSD, locationFor("web-fragment_3_1.xsd"));
|
||||
addSelf(systemIds, "web-common_3_1.xsd");
|
||||
addSelf(systemIds, "javaee_7.xsd");
|
||||
addSelf(systemIds, "jsp_2_3.xsd");
|
||||
addSelf(systemIds, "javaee_web_services_1_4.xsd");
|
||||
addSelf(systemIds, "javaee_web_services_client_1_4.xsd");
|
||||
|
||||
SERVLET_API_PUBLIC_IDS = Collections.unmodifiableMap(publicIds);
|
||||
SERVLET_API_SYSTEM_IDS = Collections.unmodifiableMap(systemIds);
|
||||
}
|
||||
|
||||
private static void addSelf(Map<String, String> ids, String id) {
|
||||
String location = locationFor(id);
|
||||
if (location != null) {
|
||||
ids.put(id, location);
|
||||
ids.put(location, location);
|
||||
}
|
||||
}
|
||||
|
||||
private static void add(Map<String,String> ids, String id, String location) {
|
||||
if (location != null) {
|
||||
ids.put(id, location);
|
||||
// BZ 63311
|
||||
// Support http and https locations as the move away from http and
|
||||
// towards https continues.
|
||||
if (id.startsWith("http://")) {
|
||||
String httpsId = "https://" + id.substring(7);
|
||||
ids.put(httpsId, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String locationFor(String name) {
|
||||
URL location = CLASS_SERVLET_CONTEXT.getResource("resources/" + name);
|
||||
if (location == null && CLASS_JSP_CONTEXT != null) {
|
||||
location = CLASS_JSP_CONTEXT.getResource("resources/" + name);
|
||||
}
|
||||
if (location == null) {
|
||||
Log log = LogFactory.getLog(DigesterFactory.class);
|
||||
log.warn(sm.getString("digesterFactory.missingSchema", name));
|
||||
return null;
|
||||
}
|
||||
return location.toExternalForm();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a <code>Digester</code> parser.
|
||||
* @param xmlValidation turn on/off xml validation
|
||||
* @param xmlNamespaceAware turn on/off namespace validation
|
||||
* @param rule an instance of <code>RuleSet</code> used for parsing the xml.
|
||||
* @param blockExternal turn on/off the blocking of external resources
|
||||
* @return a new digester
|
||||
*/
|
||||
public static Digester newDigester(boolean xmlValidation,
|
||||
boolean xmlNamespaceAware,
|
||||
RuleSet rule,
|
||||
boolean blockExternal) {
|
||||
Digester digester = new Digester();
|
||||
digester.setNamespaceAware(xmlNamespaceAware);
|
||||
digester.setValidating(xmlValidation);
|
||||
digester.setUseContextClassLoader(true);
|
||||
EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS,
|
||||
SERVLET_API_SYSTEM_IDS, blockExternal);
|
||||
digester.setEntityResolver(resolver);
|
||||
if (rule != null) {
|
||||
digester.addRuleSet(rule);
|
||||
}
|
||||
|
||||
return digester;
|
||||
}
|
||||
}
|
||||
47
java/org/apache/tomcat/util/descriptor/InputSourceUtil.java
Normal file
47
java/org/apache/tomcat/util/descriptor/InputSourceUtil.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.descriptor;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.tomcat.util.ExceptionUtils;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
public final class InputSourceUtil {
|
||||
|
||||
private InputSourceUtil() {
|
||||
// Utility class. Hide default constructor.
|
||||
}
|
||||
|
||||
|
||||
public static void close(InputSource inputSource) {
|
||||
if (inputSource == null) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
InputStream is = inputSource.getByteStream();
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.handleThrowable(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user