2012年11月30日金曜日

ClassLoaderLogManager

/**
* Per classloader LogManager implementation.
*/
public class ClassLoaderLogManager extends LogManager {

private final class Cleaner extends Thread {

@Override
public void run() {
if (useShutdownHook) {
shutdown();
}
}

}


// ------------------------------------------------------------Constructors

public ClassLoaderLogManager() {
super();
try {
Runtime.getRuntime().addShutdownHook(new Cleaner());
} catch (IllegalStateException ise) {
// We are probably already being shutdown. Ignore this error.
}
}


// -------------------------------------------------------------- Variables


/**
* Map containing the classloader information, keyed per classloader. A
* weak hashmap is used to ensure no classloader reference is leaked from
* application redeployment.
*/
protected final Map<ClassLoader, ClassLoaderLogInfo> classLoaderLoggers =
new WeakHashMap<ClassLoader, ClassLoaderLogInfo>();


/**
* This prefix is used to allow using prefixes for the properties names
* of handlers and their subcomponents.
*/
protected ThreadLocal<String> prefix = new ThreadLocal<String>();


/**
* Determines if the shutdown hook is used to perform any necessary
* clean-up such as flushing buffered handlers on JVM shutdown. Defaults to
* <code>true</code> but may be set to false if another component ensures
* that {@link #shutdown()} is called.
*/
protected volatile boolean useShutdownHook = true;


// ------------------------------------------------------------- Properties


public boolean isUseShutdownHook() {
return useShutdownHook;
}


public void setUseShutdownHook(boolean useShutdownHook) {
this.useShutdownHook = useShutdownHook;
}


// --------------------------------------------------------- Public Methods


/**
* Add the specified logger to the classloader local configuration.
*
* @param logger The logger to be added
*/
@Override
public synchronized boolean addLogger(final Logger logger) {

final String loggerName = logger.getName();

ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
if (info.loggers.containsKey(loggerName)) {
return false;
}
info.loggers.put(loggerName, logger);

// Apply initial level for new logger
final String levelString = getProperty(loggerName + ".level");
if (levelString != null) {
try {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
logger.setLevel(Level.parse(levelString.trim()));
return null;
}
});
} catch (IllegalArgumentException e) {
// Leave level set to null
}
}

// Always instantiate parent loggers so that
// we can control log categories even during runtime
int dotIndex = loggerName.lastIndexOf('.');
while (dotIndex >= 0) {
final String parentName = loggerName.substring(0, dotIndex);
Logger.getLogger(parentName);
dotIndex = loggerName.lastIndexOf('.', dotIndex - 1);
}

// Find associated node
LogNode node = info.rootNode.findNode(loggerName);
node.logger = logger;

// Set parent logger
Logger parentLogger = node.findParentLogger();
if (parentLogger != null) {
doSetParentLogger(logger, parentLogger);
}

// Tell children we are their new parent
node.setParentLogger(logger);

// Add associated handlers, if any are defined using the .handlers property.
// In this case, handlers of the parent logger(s) will not be used
String handlers = getProperty(loggerName + ".handlers");
if (handlers != null) {
logger.setUseParentHandlers(false);
StringTokenizer tok = new StringTokenizer(handlers, ",");
while (tok.hasMoreTokens()) {
String handlerName = (tok.nextToken().trim());
Handler handler = null;
ClassLoader current = classLoader;
while (current != null) {
info = classLoaderLoggers.get(current);
if (info != null) {
handler = info.handlers.get(handlerName);
if (handler != null) {
break;
}
}
current = current.getParent();
}
if (handler != null) {
logger.addHandler(handler);
}
}
}

// Parse useParentHandlers to set if the logger should delegate to its parent.
// Unlike java.util.logging, the default is to not delegate if a list of handlers
// has been specified for the logger.
String useParentHandlersString = getProperty(loggerName + ".useParentHandlers");
if (Boolean.valueOf(useParentHandlersString).booleanValue()) {
logger.setUseParentHandlers(true);
}

return true;
}


/**
* Get the logger associated with the specified name inside
* the classloader local configuration. If this returns null,
* and the call originated for Logger.getLogger, a new
* logger with the specified name will be instantiated and
* added using addLogger.
*
* @param name The name of the logger to retrieve
*/
@Override
public synchronized Logger getLogger(final String name) {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
return getClassLoaderInfo(classLoader).loggers.get(name);
}


/**
* Get an enumeration of the logger names currently defined in the
* classloader local configuration.
*/
@Override
public synchronized Enumeration<String> getLoggerNames() {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
return Collections.enumeration(getClassLoaderInfo(classLoader).loggers.keySet());
}


/**
* Get the value of the specified property in the classloader local
* configuration.
*
* @param name The property name
*/
@Override
public String getProperty(String name) {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
String prefix = this.prefix.get();
if (prefix != null) {
name = prefix + name;
}
ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
String result = info.props.getProperty(name);
// If the property was not found, and the current classloader had no
// configuration (property list is empty), look for the parent classloader
// properties.
if ((result == null) && (info.props.isEmpty())) {
ClassLoader current = classLoader.getParent();
while (current != null) {
info = classLoaderLoggers.get(current);
if (info != null) {
result = info.props.getProperty(name);
if ((result != null) || (!info.props.isEmpty())) {
break;
}
}
current = current.getParent();
}
if (result == null) {
result = super.getProperty(name);
}
}
// Simple property replacement (mostly for folder names)
if (result != null) {
result = replace(result);
}
return result;
}


@Override
public void readConfiguration()
throws IOException, SecurityException {

checkAccess();

readConfiguration(Thread.currentThread().getContextClassLoader());

}

@Override
public void readConfiguration(InputStream is)
throws IOException, SecurityException {

checkAccess();
reset();

readConfiguration(is, Thread.currentThread().getContextClassLoader());

}

@Override
public void reset() throws SecurityException {
Thread thread = Thread.currentThread();
if (thread.getClass().getName().startsWith(
"java.util.logging.LogManager$")) {
// Ignore the call from java.util.logging.LogManager.Cleaner,
// because we have our own shutdown hook
return;
}
ClassLoader classLoader = thread.getContextClassLoader();
ClassLoaderLogInfo clLogInfo = getClassLoaderInfo(classLoader);
resetLoggers(clLogInfo);
super.reset();
}

/**
* Shuts down the logging system.
*/
public void shutdown() {
// The JVM is being shutdown. Make sure all loggers for all class
// loaders are shutdown
for (ClassLoaderLogInfo clLogInfo : classLoaderLoggers.values()) {
resetLoggers(clLogInfo);
}
}

// -------------------------------------------------------- Private Methods
private void resetLoggers(ClassLoaderLogInfo clLogInfo) {
// This differs from LogManager#resetLogger() in that we close not all
// handlers of all loggers, but only those that are present in our
// ClassLoaderLogInfo#handlers list. That is because our #addLogger(..)
// method can use handlers from the parent class loaders, and closing
// handlers that the current class loader does not own would be not
// good.
synchronized (clLogInfo) {
for (Logger logger : clLogInfo.loggers.values()) {
Handler[] handlers = logger.getHandlers();
for (Handler handler : handlers) {
logger.removeHandler(handler);
}
}
for (Handler handler : clLogInfo.handlers.values()) {
try {
handler.close();
} catch (Exception e) {
// Ignore
}
}
clLogInfo.handlers.clear();
}
}

// ------------------------------------------------------ Protected Methods


/**
* Retrieve the configuration associated with the specified classloader. If
* it does not exist, it will be created.
*
* @param classLoader The classloader for which we will retrieve or build the
* configuration
*/
protected ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {

if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
}
ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);
if (info == null) {
final ClassLoader classLoaderParam = classLoader;
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
readConfiguration(classLoaderParam);
} catch (IOException e) {
// Ignore
}
return null;
}
});
info = classLoaderLoggers.get(classLoader);
}
return info;
}


/**
* Read configuration for the specified classloader.
*
* @param classLoader
* @throws IOException Error
*/
protected void readConfiguration(ClassLoader classLoader)
throws IOException {

InputStream is = null;
// Special case for URL classloaders which are used in containers:
// only look in the local repositories to avoid redefining loggers 20 times
try {
if ((classLoader instanceof URLClassLoader)
&& (((URLClassLoader) classLoader).findResource("logging.properties") != null)) {
is = classLoader.getResourceAsStream("logging.properties");
}
} catch (AccessControlException ace) {
// No permission to configure logging in context
// Log and carry on
ClassLoaderLogInfo info = classLoaderLoggers.get(ClassLoader.getSystemClassLoader());
if (info != null) {
Logger log = info.loggers.get("");
if (log != null) {
Permission perm = ace.getPermission();
if (perm instanceof FilePermission && perm.getActions().equals("read")) {
log.warning("Reading " + perm.getName() + " is not permitted. See \"per context logging\" in the default catalina.policy file.");
}
else {
log.warning("Reading logging.properties is not permitted in some context. See \"per context logging\" in the default catalina.policy file.");
log.warning("Original error was: " + ace.getMessage());
}
}
}
}
if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader())) {
String configFileStr = System.getProperty("java.util.logging.config.file");
if (configFileStr != null) {
try {
is = new FileInputStream(replace(configFileStr));
} catch (IOException e) {
// Ignore
}
}
// Try the default JVM configuration
if (is == null) {
File defaultFile = new File(new File(System.getProperty("java.home"), "lib"),
"logging.properties");
try {
is = new FileInputStream(defaultFile);
} catch (IOException e) {
// Critical problem, do something ...
}
}
}

Logger localRootLogger = new RootLogger();
if (is == null) {
// Retrieve the root logger of the parent classloader instead
ClassLoader current = classLoader.getParent();
ClassLoaderLogInfo info = null;
while (current != null && info == null) {
info = getClassLoaderInfo(current);
current = current.getParent();
}
if (info != null) {
localRootLogger.setParent(info.rootNode.logger);
}
}
ClassLoaderLogInfo info =
new ClassLoaderLogInfo(new LogNode(null, localRootLogger));
classLoaderLoggers.put(classLoader, info);

if (is != null) {
readConfiguration(is, classLoader);
}
addLogger(localRootLogger);

}


/**
* Load specified configuration.
*
* @param is InputStream to the properties file
* @param classLoader for which the configuration will be loaded
* @throws IOException If something wrong happens during loading
*/
protected void readConfiguration(InputStream is, ClassLoader classLoader)
throws IOException {

ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);

try {
info.props.load(is);
} catch (IOException e) {
// Report error
System.err.println("Configuration error");
e.printStackTrace();
} finally {
try {
is.close();
} catch (Throwable t) {}
}

// Create handlers for the root logger of this classloader
String rootHandlers = info.props.getProperty(".handlers");
String handlers = info.props.getProperty("handlers");
Logger localRootLogger = info.rootNode.logger;
if (handlers != null) {
StringTokenizer tok = new StringTokenizer(handlers, ",");
while (tok.hasMoreTokens()) {
String handlerName = (tok.nextToken().trim());
String handlerClassName = handlerName;
String prefix = "";
if (handlerClassName.length() <= 0) {
continue;
}
// Parse and remove a prefix (prefix start with a digit, such as
// "10WebappFooHanlder.")
if (Character.isDigit(handlerClassName.charAt(0))) {
int pos = handlerClassName.indexOf('.');
if (pos >= 0) {
prefix = handlerClassName.substring(0, pos + 1);
handlerClassName = handlerClassName.substring(pos + 1);
}
}
try {
this.prefix.set(prefix);
Handler handler =
(Handler) classLoader.loadClass(handlerClassName).newInstance();
// The specification strongly implies all configuration should be done
// during the creation of the handler object.
// This includes setting level, filter, formatter and encoding.
this.prefix.set(null);
info.handlers.put(handlerName, handler);
if (rootHandlers == null) {
localRootLogger.addHandler(handler);
}
} catch (Exception e) {
// Report error
System.err.println("Handler error");
e.printStackTrace();
}
}

}

}


/**
* Set parent child relationship between the two specified loggers.
*
* @param logger
* @param parent
*/
protected static void doSetParentLogger(final Logger logger,
final Logger parent) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
logger.setParent(parent);
return null;
}
});
}


/**
* System property replacement in the given string.
*
* @param str The original string
* @return the modified string
*/
protected String replace(String str) {
String result = str;
int pos_start = result.indexOf("${");
if (pos_start != -1) {
int pos_end = result.indexOf('}');
if (pos_end != -1) {
String propName = result.substring(pos_start + 2, pos_end);
String replacement = System.getProperty(propName);
if (replacement != null) {
if(pos_start >0) {
result = result.substring(0,pos_start) +
replacement + replace(result.substring(pos_end + 1));
} else {
result = replacement + replace(result.substring(pos_end + 1));
}
}
}
}
return result;
}


// ---------------------------------------------------- LogNode Inner Class


protected static final class LogNode {
Logger logger;

protected final Map<String, LogNode> children =
new HashMap<String, LogNode>();

protected final LogNode parent;

LogNode(final LogNode parent, final Logger logger) {
this.parent = parent;
this.logger = logger;
}

LogNode(final LogNode parent) {
this(parent, null);
}

LogNode findNode(String name) {
LogNode currentNode = this;
if (logger.getName().equals(name)) {
return this;
}
while (name != null) {
final int dotIndex = name.indexOf('.');
final String nextName;
if (dotIndex < 0) {
nextName = name;
name = null;
} else {
nextName = name.substring(0, dotIndex);
name = name.substring(dotIndex + 1);
}
LogNode childNode = currentNode.children.get(nextName);
if (childNode == null) {
childNode = new LogNode(currentNode);
currentNode.children.put(nextName, childNode);
}
currentNode = childNode;
}
return currentNode;
}

Logger findParentLogger() {
Logger logger = null;
LogNode node = parent;
while (node != null && logger == null) {
logger = node.logger;
node = node.parent;
}
return logger;
}

void setParentLogger(final Logger parent) {
for (final Iterator<LogNode> iter =
children.values().iterator(); iter.hasNext();) {
final LogNode childNode = iter.next();
if (childNode.logger == null) {
childNode.setParentLogger(parent);
} else {
doSetParentLogger(childNode.logger, parent);
}
}
}

}


// -------------------------------------------- ClassLoaderInfo Inner Class


protected static final class ClassLoaderLogInfo {
final LogNode rootNode;
final Map<String, Logger> loggers = new HashMap<String, Logger>();
final Map<String, Handler> handlers = new HashMap<String, Handler>();
final Properties props = new Properties();

ClassLoaderLogInfo(final LogNode rootNode) {
this.rootNode = rootNode;
}

}


// ------------------------------------------------- RootLogger Inner Class


/**
* This class is needed to instantiate the root of each per classloader
* hierarchy.
*/
protected class RootLogger extends Logger {
public RootLogger() {
super("", null);
}
}


}

Mailログハンドラ初期化エラー/Eclipse WTP

AXISを利用してウェブサービスを開発するとき、下記のように自前のMailログハンドラを初期化しょうとする場合、通常のTomcatでは問題なく動作しますが、ElipseでWTPで動作しょうとすると、なぜかエラーが起こってログハンドラの初期化が出来なくなりました。

原因を判明すべく色々と調査しました。解決策としては下記のパラメータをTomcatの実行コマンドに渡せば、エラーが解消できました。
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager

2012年11月28日水曜日

アクセス権のない場所にあるリソース

java.class.pathに設定した場所からは、たとえFilePermissionがなかったとしてもClassLoaderのfindClass()を使ってクラスをロードすることができる。それではgetResource()やgetResourceAsStream()で直接データを取得することはできるのだろうか?と疑問に思って、SunのJDK (JDK1.6)で実験してみた。
実験内容と結果
import java.io.*;
import java.net.*;

public class ResourceTest
{
public static void main(String[] args)throws Exception
{
ClassLoader cl = ResourceTest.class.getClassLoader();
URL url = cl.getResource(args[0]);
System.out.println("URL=" + url);
InputStream ins = cl.getResourceAsStream(args[0]);
ins.close();
}
}
このプログラムは、引数に指定した名前を持つリソースのURLを表示後、InputStreamを取得している。
たとえば、以下のように引数を指定して起動すると、java/lang/Objec.classを読み取ることができる。
java ResourceTest java/lang/Object.class
実行結果
URL=jar:file:/C:/Program%20Files/Java/jre6/lib/rt.jar!/java/lang/Object.class
rt.jarに対するURLを取得できている。
しかし、以下のようにSecurityManagerを設定した場合には、
java -Djava.security.manager ResourceTest java/lang/Object.class
nullが返ってきてアクセスできない。
URL=null
Exception in thread "main" java.lang.NullPointerException
at ResourceTest.main(ResourceTest.java:12)
ClassLoaderクラスのgetResource()メソッドとgetResourceAsStream()メソッドはどちらもnullを返してくる。
結論
SecurityManagerが設定されている場合、java.class.path(およびブートストラップクラスパス)内のリソースをClassLoader.getResource()呼び出し時にアクセスチェックが行われているらしい。
アクセス権違反が検出された場合、AccessControlExceptionは発生せず、単純にnullが返ってくる。
疑問
この動作は仕様なのか?少なくともJDKのドキュメント(ClassLoader (Java Platform SE 6)、SecureClassLoader (Java Platform SE 6)、URLClassLoader (Java Platform SE 6)には記載されていない。
追記)仕様だった。以下の場所に記述がある。
http://www.j2ee.me/javase/ja/6/docs/ja/technotes/guides/lang/resources.html#security
getResource() は、情報へのアクセスを提供するため、セキュリティーの規則が適切に定義および構築されている必要があります。セキュリティー上の配慮により、あるセキュリティーコンテキストであるリソースへのアクセスが許可されていない場合は、getResource() メソッドは、あたかもそのリソースが存在しないかのように失敗する (null を返す) ようになっています。 これは、存在性攻撃に対する配慮です。

Location-Independent Access to Resources( getResourceAsStream return null when custom of SecurityManager )


http://download.java.net/jdk8/docs/technotes/guides/lang/resources.html

Overview

A resource is data (images, audio, text, and so on) that a program needs to access in a way that is independent of the location of the program code. Java programs can use two mechanisms to access resources: Applets use Applet.getCodeBase() to get the base URL for the applet code and then extend the base URL with a relative path to load the desired resource, for example with Applet.getAudioClip(url). Applications use "well known locations" such as System.getProperty("user.home") or System.getProperty("java.home"), then add "/lib/resource", and open that file.

Methods in the classes Class and ClassLoader provide a location-independent way to locate resources. For example, they enable locating resources for:

  • An applet loaded from the Internet using multiple HTTP connections.
  • An applet loaded using JAR files.
  • A Java Bean loaded or installed in the CLASSPATH.
  • A "library" installed in the CLASSPATH.

These methods do not provide specific support for locating localized resources. Localized resources are supported by the internationalization facilities.

Resources, names, and contexts

A resource is identified by a string consisting of a sequence of substrings, delimited by slashes (/), followed by a resource name. Each substring must be a valid Java identifier. The resource name is of the form shortName or shortName.extension. Both shortName and extension must be Java identifiers.

The name of a resource is independent of the Java implementation; in particular, the path separator is always a slash (/). However, the Java implementation controls the details of how the contents of the resource are mapped into a file, database, or other object containing the actual resource.

The interpretation of a resource name is relative to a class loader instance. Methods implemented by the ClassLoader class do this interpretation.

System Resources

A system resource is a resource that is either built-in to the system, or kept by the host implementation in, for example, a local file system. Programs access system resources through the ClassLoader methods getSystemResource and getSystemResourceAsStream.

For example, in a particular implementation, locating a system resource may involve searching the entries in the CLASSPATH. The ClassLoadermethods search each directory, ZIP file, or JAR file entry in the CLASSPATH for the resource file, and, if found, returns either an InputStream, or the resource name. If not found, the methods return null. A resource may be found in a different entry in the CLASSPATH than the location where the class file was loaded.

Non-System Resources

The implementation of getResource on a class loader depends on the details of the ClassLoader class. For example, AppletClassLoader:

  • First tries to locate the resource as a system resource; then, if not found,
  • Searches through the resources in archives (JAR files) already loaded in this CODEBASE; then, if not found,
  • Uses CODEBASE and attempts to locate the resource (which may involve contacting a remote site).

All class loaders will search for a resource first as a system resource, in a manner analogous to searcing for class files. This search rule permits overwriting locally any resource. Clients should choose a resource name that will be unique (using the company or package name as a prefix, for instance).

Resource Names

A common convention for the name of a resource used by a class is to use the fully qualified name of the package of the class, but convert all periods (.) to slashes (/), and add a resource name of the form name.extension. To support this, and to simplify handling the details of system classes (for which getClassLoader returns null), the class Class provides two convenience methods that call the appropriate methods inClassLoader.

The resource name given to a Class method may have an initial starting "/" that identifies it as an "absolute" name. Resource names that do not start with a "/" are "relative".

Absolute names are stripped of their starting "/" and are passed, without any further modification, to the appropriate ClassLoader method to locate the resource. Relative names are modified according to the convention described previously and then are passed to a ClassLoader method.

Using Methods of java.lang.Class

The Class class implements several methods for loading resources.

The method getResource() returns a URL for the resource. The URL (and its representation) is specific to the implementation and the JVM (that is, the URL obtained in one runtime instance may not work in another). Its protocol is usually specific to the ClassLoader loading the resource. If the resource does not exist or is not visible due to security considerations, the methods return null.

If the client code wants to read the contents of the resource as an InputStream, it can apply the openStream() method on the URL. This is common enough to justify adding getResourceAsStream() to Class and ClassLoader. getResourceAsStream() the same as callinggetResource().openStream(), except that getResourceAsStream() catches IO exceptions returns a null InputStream.

Client code can also request the contents of the resource as an object by applying the java.net.URL.getContent() method on the URL. This is useful when the resource contains the data for an image, for instance. In the case of an image, the result is an awt.image.ImageProducer object, not an Image object.

The getResource and getResourceAsStream methods find a resource with a given name. They return null if they do not find a resource with the specified name. The rules for searching for resources associated with a given class are implemented by the class's ClassLoader. The Classmethods delegate to ClassLoader methods, after applying a naming convention: if the resource name starts with "/", it is used as is. Otherwise, the name of the package is prepended, after converting all periods (.) to slashes (/).

public InputStream getResourceAsStream(String name) {    name = resolveName(name);    ClassLoader cl = getClassLoader();    if (cl==null) {      return ClassLoader.getSystemResourceAsStream(name); // A system class.    }    return cl.getResourceAsStream(name);  }    public java.net.URL getResource(String name) {    name = resolveName(name);    ClassLoader cl = getClassLoader();    if (cl==null) {      return ClassLoader.getSystemResource(name);  // A system class.    }    return cl.getResource(name);  }  

The resolveName method adds a package name prefix if the name is not absolute, and removes any leading "/" if the name is absolute. It is possible, though uncommon, to have classes in diffent packages sharing the same resource.

private String resolveName(String name) {    if (name == null) {      return name;    }    if (!name.startsWith("/")) {      Class c = this;      while (c.isArray()) {        c = c.getComponentType();      }      String baseName = c.getName();      int index = baseName.lastIndexOf('.');      if (index != -1) {        name = baseName.substring(0, index).replace('.', '/') + "/" + name;      }    } else {      name = name.substring(1);    }    return name;  }  

Using Methods of java.lang.ClassLoader

The ClassLoader class has two sets of methods to access resources. One set returns an InputStream for the resource. The other set returns a URL. The methods that return an InputStream are easier to use and will satisfy many needs, while the methods that return URLs provide access to more complex information, such as an Image and an AudioClip.

The ClassLoadermanges resources similarly to the way it manages classes. A ClassLoader controls how to map the name of a resource to its content. ClassLoader also provides methods for accessing system resources, analogous to the system classes. The Class class provides some convenience methods that delegate functionality to the ClassLoader methods.

Many Java programs will access these methods indirectly through the I18N (localization) APIs. Others will access it through methods in Class. A few will directly invoke the ClassLoader methods.

The methods in ClassLoader use the given String as the name of the resource without applying any absolute/relative transformation (see the methods in Class). The name should not have a leading "/".

System resources are those that are handled by the host implemenation directly. For example, they may be located in the CLASSPATH.

The name of a resource is a "/"-separated sequence of identifiers. The Class class provides convenience methods for accessing resources; the methods implement a convention where the package name is prefixed to the short name of the resource.

Resources can be accessed as an InputStream, or a URL.

The getSystemResourceAsStream method returns an InputStream for the specified system resource or null if it does not find the resource. The resource name may be any system resource.

The getSystemResource method finds a system resource with the specified name. It returns a URL to the resource or null if it does not find the resource. Calling java.net.URL.getContent() with the URL will return an object such as ImageProducer, AudioClip, or InputStream.

The getResourceAsStream method returns an InputStream for the specified resource or null if it does not find the resource.

The getResource method finds a resource with the specified name. It returns a URL to the resource or null if it does not find the resource. Callingjava.net.URL.getContent() with the URL will return an object such as ImageProducer, AudioClip, or InputStream.

Security

Since getResource() provides access to information, it must have well-defined and well-founded security rules. If security considerations do not allow a resource to be visible in some security context, the getResource() method will fail (return null) as if the resource were not present at all, this addresses existence attacks.

Class loaders may not provide access to the contents of a .class file for both security and performance reasons. Whether it is possible to obtain a URL for a .class file depends on the specifics, as shown below.

There are no specified security issues or restrictions regarding resources that are found by a non-system class loader. AppletClassLoaderprovides access to information that is loaded from a source location, either individually, or in a group through a JAR file; thus AppletClassLoadershould apply the same checkConnect() rules when dealing with URLs through getResource().

The system ClassLoader provides access to information in the CLASSPATH. A CLASSPATH may contain directories and JAR files. Since a JAR file is created intentionally, it has a different significance than a directory where things may end up in a more casual manner. In particular, we are more strict on getting information out of a directory than out from a JAR file.

If a resource is in a directory:

  • getResource() invocations will use File.exists() to determine whether to make the corresponding file visible to the user. Recall thatFile.exists() uses the checkRead() method in the security manager.
  • the same applies to getResourceAsStream().

If the resource is in a JAR file:

  • getResource() invocations will succeed for all files, regardless of whether the invocation is done from within a system or a non-system class.
  • getResourceAsStream() invocations will succeed for non .class resources, and so will for java.net.URL.getContent() on corresponding URLs.

Examples

This section provides two examples of client code. The first example uses "absolute resource" names and traditional mechanisms to get a Classobject.

package pkg;    import java.io.IOException;  import java.io.InputStream;  import java.io.PrintStream;    class Test {      private static final String absName = "/pkg/mumble.baf";      public static void test1() {      Class c=null;      try {        c = Class.forName("pkg.Test");      } catch (Exception ex) {        // This should not happen.      }      InputStream s = c.getResourceAsStream(absName);      // do something with it.    }      public void test2() {      InputStream s = this.getClass().getResourceAsStream(absName);    // do something with it.    }  }  

This example uses "relative resource" names and the mechanism available from the compiler through the -experimental flag, to get a Classobject.

package pkg;    import java.io.IOException;  import java.io.InputStream;  import java.io.PrintStream;    class Test {    private static final String relName = "mumble.baf";    public static void test1() {    InputStream s = Test.class.getResourceAsStream(relName);    // do something with it.  }      public void test2() {      InputStream s = Test.class.getResourceAsStream(relName);      // do something with it.    }  

API References

Eclipse: Access restriction: The method createJPEGEncoder(OutputStream) from the type JPEGCodec is not accessible due to restriction on required library

Imported some source code in a Eclipse java project, and got error:

Access restriction: The method createJPEGEncoder(OutputStream) from the type JPEGCodec is not accessible due to restriction on required library /usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/rt.jar  

The fix it:

  1. Right-click the project, and click "Properties"
  2. open "Java Compiler", 
  3. select "Errors/Warnings",
  4. "Enable project specific settings", 
  5. open "Deprecated and restricted API"
  6. change to "Warning" for "Forbidden reference(access rules)"

2012年11月27日火曜日

PDFBox create pdf from Graphics2d

- create a document
- add a page
- create a PDJpeg from a BufferedImage of the Graphics2D object
- add the PDJpeg to your page
- save the document

public static void main(String... _) throws Exception {
PDDocument doc = null;
try {
doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage(page);
PDXObjectImage ximage = null;

BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawString("Hallo PDFBox", 100, 100);
g.dispose();
ximage = new PDJpeg(doc, image);

/* Lade ein Bild */
// ximage = new PDJpeg(doc, new FileInputStream( "PFAD" ) );

PDPageContentStream contentStream = new PDPageContentStream(doc,page);
contentStream.drawImage(ximage, 20, 20);
contentStream.close();
doc.save("PFAD WO PDF GESPEICHERT WERDEN SOLL"); //anpassen
} finally {
if (doc != null) {
doc.close();
}
}
}

Java で、ランダムな文字列を生成する方法

まずJavaにはランダムを提供するクラスとして

java.util.Random
java.security.SecureRandom
の2つがあってRandomクラスは以下のような強度しか持っていない。

Random クラスのインスタンスは、一連の擬似乱数を生成します。クラスでは 48 ビットのシードを使い、このシードは線形合同法で変更されます。
no title
しかも初期化があまりよくなくて初回の乱数がとても偏る。

Random r = new java.util.Random();
double x, min = 1, max = 0;
for (int i = 0; i < 1024; i++) {
r.setSeed(i);
x = r.nextDouble();
if (x > max)
max = x;
if (x < min)
min = x;
}
System.out.println("Min=" + min + " Max=" + max);
Min=0.6753377750582709 Max=0.7669794809891215

こんなプログラムを回してみるとと1000通りの種を渡しても初回生成値が極度に偏ってしまう。

んで、SecureRandomはこのあたりの問題を解消するのでててきた。

for (int i = 0; i < 1024; i++) {
sr.setSeed(i);
x = sr.nextDouble();
if (x > max){
max = x;
}
if (x < min){
min = x;
}
}
BigDecimal bigMin = new BigDecimal(min).setScale(16,BigDecimal.ROUND_HALF_UP);
System.out.println("Min=" + bigMin + " Max=" + max);
x = 0.0;
Min=0.0004351500205996 Max=0.9986417236968099

SecureRandomにすると一気に分散するようになった。



ランダムな文字列を生成するには

そもそもランダムな文字列を作る為にRandomを久々にみてたわけですが、

今普通にランダムな文字列を使うのならcommons-langのRandomStringUtilsで事足りる。

アスキー文字なら

RandomStringUtils.randomAscii(10)
⇒DF|M1!7W=@
アルファベットなら

RandomStringUtils.randomAlphabetic(10)
⇒doKntQIMve
数字なら(0始まりも生成されてしまうので注意)

RandomStringUtils.randomNumeric(10)
⇒8266471697
頭0を嫌うなら
RandomStringUtils.random(1,"123456789") + RandomStringUtils.randomNumeric(9);
自分で指定した文字の中からランダムにしたいのなら

RandomStringUtils.random(10,"012345abcdef");
⇒c021cc0b24
長さもランダムにしたいのならRandomUtils.nextIntと組み合わせて(1〜3文字のランダムな文字が変える)

RandomStringUtils.random(RandomUtils.nextInt(3)+1,"12345678");
⇒1
⇒255
⇒86

Java帳票 iTextで高解像度の画像を印刷する

  • Javaの標準の印刷 (JPS) で画像を印刷する手段は一長一短
    利点 欠点
    java.awt.print.PrinterJob? どんなプリンタでも使える 解像度が72dpi
    javax.print.DocPrinterJob? 高解像度の印刷可能 プリンタドライバによっては、直接、画像を印刷できない
  • iTextで、高解像度の画像をPDFに貼り付けて印刷すれば上手くといいな

ソースコード 

  • 1000px x 1000px の画像を PDF に貼り付けた
  • 画像の解像度は img.scalePercent(float); で指定できる。 画像を縮小するとその分解像度が上がる
    縮尺(%) 解像度(dpi)
    100f 72dpi
    50f 144dpi
    25f 288dpi
    10f 720dpi
    5f 1440dpi
  • 無指定時は 72 dpi
  • 画像の大きさ(縦横の長さ)が小さくなっても画素情報は失われない。たとえば、 (1inch x 1inch) の (1000px x 1000px) の画像になる。



package com.snail.exam.printexam;

import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfWriter;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;

public class ITextExam3 {

    public static void main(String[] args) {
        try {

            OutputStream out = new FileOutputStream("/tmp/iTextExample3.pdf");
            //ByteArrayOutputStream out = new ByteArrayOutputStream();
           
            Document doc = new Document(PageSize.A4, 50, 50, 50, 50);
            PdfWriter pdfwriter = PdfWriter.getInstance(doc, out);

            Image img = createImage();

            doc.open();
           
            doc.add(new Phrase("72dpi\n\n"));
            doc.add(img);
           
            doc.newPage();
            img.scalePercent(50f);
            doc.add(new Phrase("144dpi\n\n"));
            doc.add(img);
           
            doc.newPage();
            img.scalePercent(25f);
            doc.add(new Phrase("288dpi\n\n"));
            doc.add(img);
           
            doc.newPage();
            img.scalePercent(10f);
            doc.add(new Phrase("720dpi\n\n"));
            doc.add(img);
           
            doc.newPage();
            img.scalePercent(5f);
            doc.add(new Phrase("1440dpi\n\n"));
            doc.add(img);
           
            // Document End
            doc.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
   
    private static Image createImage() throws IOException, BadElementException{
            BufferedImage image = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);

            Graphics2D g = image.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            g.setColor(Color.WHITE);
            g.fillRect(0, 0, image.getWidth(), image.getHeight());

            g.setColor(Color.GRAY);
            for (int x = 0; x < image.getWidth(); x += 5) {
                g.drawLine(x, 0, x, image.getHeight());
            }
            for (int y = 0; y < image.getHeight(); y += 5) {
                g.drawLine(0, y, image.getWidth(), y);
            }

            g.setColor(Color.BLACK);
            g.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 24));
            g.drawString("20pt 123 ABC あいう アイウ", 0, 20);

            g.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 10));
            g.drawString("10pt 123 ABC あいう アイウ", 0, 40);

            g.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 8));
            g.drawString("8pt 123 ABC あいう アイウ", 0, 60);
           
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(image, "png", out);
           
            return com.itextpdf.text.Image.getInstance(out.getBytes()); 
    }
}

IText Image オブジェクト

iText は、さまざまなイメージフォーマット(いくつかのフォーマットはイメージに限定されていません)をサポートしています。
  • JPEG: Joint Photographic Experts Group. 非可逆な圧縮法として一般的に使われており、96%程度サイズを圧縮する。Web に写真を掲載するためにはもっとも優れている。
  • GIF: Graphic Interchange Format. イメージファイルの共通フォーマット、特にイメージの多くの部分が同じ色の場合に使われる。単純なイメージの GIF フォーマットファイルは、JPEG フォーマットのものよりも小さくなることが多いですが、写真は JPEG のようには保存されません。
  • PNG: Portable Network Graphics. GIFの後継フォーマットとしてデザインされた。GIF のように、圧縮、透過、プログレッシブローディングをサポートしており、特許の制約からフリーです。
  • TIFF: Tagged Image File Format. デジタルスキャナで取り込まれたイメージのための共通フォーマットとして使われている。
  • BMP: Windows bitmap. Microsoft Windows においてビットマップファイルの共通フォーマット。他のオペレーティングシステムでは、色に関する事項は十分にはサポートされていない。
  • WMF:  Windows Metafile Format. Windows コンパチブルコンピュータのベクタグラフィックスフォーマットであり、ワードプロセッサのクリップアートとしてよく使われる。
  • EPS の基本的な機能:  Encapsulated PostScript. PostScript 言語においてイメージを記述するためのフォーマット。
  • java.awt.Image: JAVA オブジェクト。SUNのJDKに含まれている。
  • com.lowagie.text.pdf.PdfTemplate: PdfTemplate で定義された PDF 文は、Image のなかにラップすることができます。
  • com.lowagie.text.pdf.Barcode: 抽象クラスです。どのようなバーコードがサポートされているかは、実装クラスを参照してください。
Image インスタンスは、getInstance メソッドのうちのひとつを使って作成されます。Image は抽象クラスなので、getInstance メソッドは、与えられたイメージの型 (Jpeg、WMFなど) を判断し、それに従ったオブジェクト (JpegImgWMFなど) を返します。多くのPDFライブラリは、イメージを復号してPDFにする前に、ビットマップのようなフォーマットに変換してしまいます。このアプローチは、生成されるPDFが非常に大きくなってまうので、iText ではこのような方法をとっていません。ほかのライブラリでは、もとのファイルサイズの 10倍 にもなってしまうこともありますが、iText ではそんなことにはなりません。同一の Image は、再利用されます (Image XObject はひとつしかありません) 。
Example: java com.lowagie.examples.objects.images.Images
Adds different formats of images to a document.: see Images.pdf
External resources for this example: otsoe.jpg iText.bmp pngnow.png iText.tif getacro.gifiText.wmf

Image インスタンスを得るもっとも普通の方法は、ファイルのパス (getInstance(java.lang.String)) か URL (getInstance(java.net.URL)) を使う方法ですが、Raw イメージデータ (例えば、データベースにイメージデータが格納されている場合など) から作成することもできます。

Example: java com.lowagie.examples.objects.images.RawData
Using raw image data to construct an Image object.: see rawdata.pdf
External resources for this example: otsoe.jpg
iText は、JAVA のライブラリなので、java.awt.Image オブジェクトもサポートしています。しかし、この機能を UNIX/Linux システムで使うときには気をつけてください。java.awt.Image をインスタンス化するための、java.awt.Toolkit を使うときには、特別な X Window が必要です。com.lowagie.text.Image に固執すれば、ほかに必要なものはなくなります。com.lowagie.text.Image は、どのプラットフォームでも利用できます。
Example: java com.lowagie.examples.objects.images.AwtImage
Using a java.awt.Image object to construct an Image object.: see awt_image.pdf
External resources for this example: H.gif
先頭に戻る
イメージの配置
イメージの配置は、setAlignment(int) によって設定できます。パラメータとして、意味が自明な定数 LEFTRIGHT あるいは MIDDLE をとることができます。
Example: java com.lowagie.examples.objects.images.Alignment
Alignment of images.: see alignment.pdf
External resources for this example: vonnegut.gif otsoe.jpg hitchcock.png
これらの定数とともに、 TEXTWRAP  UNDERLYING 定数も組み合わせることができます。最初の定数は、テキストをイメージの周りに配置し、二番目の定数は、テキストはイメージの上に配置されます。どちらの定数も、次の例で、示されています。
Example: java com.lowagie.examples.objects.images.ImagesAlignment
Alignment and wrapping of images.: see imagesAlignment.pdf
External resources for this example: vonnegut.gif otsoe.jpg hitchcock.png
イメージの絶対位置が指定されていないときには、iText は、現在のポインタに配置しようとします。しかし、イメージを加えるために必要な空間がない場合もあります。そのような場合、iText はイメージを加えることを延期し、ほかのコンテンツを先に加えます。イメージを add メソッドを呼び出したそのときに加えたいときには、Writer に正確な順序でイメージを配置することを設定しなければなりません。
writer.setStrictImageSequence(true);
例で、iTextの既定の振る舞いと、正確な順序でイメージを配置することを設定した場合の違いを見てください。
Example: java com.lowagie.examples.objects.images.ImageSequence
Adds images to a document, once respecting the order in which they were added, once in the default order.: see inSequence.pdf NotInSequence.pdf
External resources for this example: otsoe.jpg getacro.gif
先頭に戻る
イメージの変換
ほかのコンテンツにかかわりなく、イメージをページ上の絶対位置に加えることができます。座標系座標系の章において、変換行列の仕組みについて詳しく説明しますが、それは詳しすぎるものでしょう。通常使われる、拡大、縮小、単純な回転などの単純な操作には、そのような計算が必要になることはほとんどありません。

変換
座標系について知っておかなければならないことは、原点が左下にあるということです。etAbsolutePosition(float, float) メソッドのパラメータは、イメージの左下を定義します。
Example: java com.lowagie.examples.objects.images.AbsolutePositions
Adding an Image at absolute positions.: see absolutepositions.pdf
External resources for this example: hitchcock.png
スケーリング(拡大・縮小)
イメージの大きさを指定してスケーリングをするときには、scaleAbsolute[Width|Height] メソッドを使います。ある割合 (パーセント) でスケーリングするときは、scalePercent メソッドを使います。
既定では、スケーリングされていないイメージは、解像度が 72dpi と扱われることに注意。50% でスケーリングされたイメージでは、144 になります。小さくスケーリングされたイメージは、ピクセルの大きさが小さくなるので、解像度は大きくなりますを、300dpi で出力したいときには、72/300=24% のスケーリングを使います。例えば、24% (72/300 = 0.24) のスケーリングで 1500X1500 ピクセル (5X300 = 1500) のイメージを PDF へ配置すると、PDF 中では、1500X1500 ピクセル が 5X5 inch となり解像度が 300dpi のイメージとなります。サイズがどうあろうとも、そのイメージはいつでも 1500X1500 ピクセルです。
Example: java com.lowagie.examples.objects.images.Scaling
Scaling images.: see scaling.pdf
External resources for this example: otsoe.jpg
ほかに、使いでのあるメソッドとして、scaleToFit があります。このメソッドは、イメージを四角形に合うようにスケーリングしますが、XY の比は保持します。次の例の権利は、私自身が所有しているものではありません。www.bigfoto.com に感謝。
Example: java com.lowagie.examples.objects.images.DvdCover dvdcover.pdf My Sunflower Movie 808080 sunflower-front.jpg sunflower-back.jpg
Make a DVD Cover.: see dvdcover.pdf
External resources for this example: sunflower-front.jpg sunflower-back.jpg
回転
イメージは、 setRotation(float) (角度はラジアン) あるいは setRotationDegrees(float) (角度は度) メソッドのうちのひとつで回転ができます。
Example: java com.lowagie.examples.objects.images.Rotating
Rotating images.: see rotating.pdf
External resources for this example: otsoe.jpg
先頭に戻る
特別な機能
クリック可能なイメージ
クリック可能なイメージやイメージに注釈を加えたいときは、Annotation オブジェクトを構築し、それをイメージに加えなければならない。位置は、必ずしも指定する必要はない (0, 0, 0, 0 でもよい)。位置は、内部でイメージに合うように変換される。setAnnotation(com.lowagie.text.Annotation) メソッドと注釈の章を参照ください。
Example: java com.lowagie.examples.objects.images.AnnotatedImage
images and annotations.: see annotated_images.pdf
External resources for this example: otsoe.jpg iText.wmf
Chunk 中のイメージ
In some cases it can be handy to wrap an image inside a . Just create a Chunk with an image and an offset with one of the Chunk constructors that take an Image as parameter. 場合によっては、イメージをChunk 中に入れることによってそれをラップすることもできます。Image をパラメータとしてとる Chunk のコンストラクタによってイメージとそのオフセットを保持する Chunk を作成するだけです。
Example: java com.lowagie.examples.objects.images.ImageChunks
Images wrapped in a Chunk.: see imageChunks.pdf
External resources for this example: pngnow.png
イメージマスク
ほかのイメージの透過性を指定するマスクとして使われる Image を作成することができます。
3C
7E
E7
C3
C3
E7
7E
3C
このイメージは、たて8ピクセル、横8ピクセル、1コンポーネントで、コンポーネントあたり1バイトです。makeMask() メソッドを使うことにより、このイメージはマスクになります。
byte maskr[] =     {(byte)0x3c,      (byte)0x7e,      (byte)0xe7,      (byte)0xc3,      (byte)0xc3,      (byte)0xe7,      (byte)0x7e,      (byte)0x3c};  Image mask = Image.getInstance(8, 8, 1, 1, maskr);  mask.makeMask();  mask.setInvertMask(true);
このマスクを、イメージの一部をクリップするための明示的なマスクとして利用できます。
PdfContentByte cb = writer.getDirectContent();  Image image = Image.getInstance("vonnegut.gif");  image.setImageMask(mask);
色とパタンの章では、ステンシルマスクの概念について論じます。Image から作成したマスクは、このようにステンシルマスクにも使えます。
PdfContentByte cb = writer.getDirectContent();  cb.setRGBColorFill(255, 0, 0);  cb.addImage(mask, mask.scaledWidth() * 8, 0, 0, mask.scaledHeight() * 8, 100, 400);
 (addImage メソッドについては、座標系などについての知識も必要です。。)
Example: java com.lowagie.examples.objects.images.ImageMasks
Applying a mask to an image.: see maskedImages.pdf
External resources for this example: otsoe.jpg