You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Luca Arzeni <l....@iname.com> on 2014/06/17 11:44:01 UTC
Editing tapestry wiki to add a ClassPathUrlConverter for
JBoss-eap-6.1.1.GA (Jboss-as-7.2.1)
package com.example.project.services;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This converter works for Tapestry 5.3.7 under jboss-eap-6.1.1.GA, which is based on jboss-as-7.2.1, so it should work
* even under that release.
*
* @author l dot arzeni -at- iname dot com
*/
public class ClasspathURLConverterJBoss612Eap implements ClasspathURLConverter {
private final static String JBOSS_VFS_PREFIX="vfs";
private final static String JBOSS_TEMPORARY_FOLDER="/opt/jboss-eap-6.1.1.GA/standalone/tmp";
private final static String JBOSS_VFS_310_GET_PHYSICAL_FILE_METHOD_NAME="getPhysicalFile";
private final static Logger logger = LoggerFactory.getLogger(ClasspathURLConverterJBoss612Eap.class);
public URL convert(URL url) {
assert (url != null) : "Error: url to convert should not be null!";
// If the URL is not a "vfs" URL (JBoss 5 and newer uses a Virtual File System),
// let it pass without further processing
if (! url.getProtocol().startsWith(JBOSS_VFS_PREFIX)) {
return url; // leave the url unchanged
}
logger.debug("Processing url "+ url);
if (logger.isDebugEnabled()) {
printFolderContents(JBOSS_TEMPORARY_FOLDER);
}
return getPhysicalUrl(url);
}
/**
* To retrieve the archive physical path, we look for the rightmost "archive" component of the path.
* Actually, since we assume to deploy an ear, we assume that we will find a "/lib/xxx.jar" or a "/yyy.war"
* (the "/yyyy.war/WEB-INF/lib/zzz.jar" has not been tested, but should work).
*
* When we have obtained the vfs path of the rightmost archive, we ask to vfs the physical path of the mounted
* archive; then we append to it the "package" part of the url.
*
* @param i_vfsUrl the "vfs:" url to be analyzed
*
* @return the url to the physical archive/component, null if there are errors
*
* Examples:
* JAR:
* IN: vfs:/content/example.ear/lib/tapestry-core-5.3.7.jar/org/apache/tapestry5/corelib/components/OutputRaw.class
* OUT: jar:file:/opt/jboss-eap-6.1.1.GA/standalone/tmp/vfs/deployment25da2658a1e1e74e/tapestry-core-5.3.7.jar-607f5c47254d148c/tapestry-core-5.3.7.jar!/org/apache/tapestry5/corelib/components/OutputRaw.class
*
* WAR:
* IN: vfs:/content/example.ear/example.war/WEB-INF/classes/com/example/project/components/
* OUT: file:/opt/jboss-eap-6.1.1.GA/standalone/tmp/vfs/deployment25da2658a1e1e74e/example.war-3c5a10a04f50bfe9/WEB-INF/classes/com/example/project/components/
*
*/
private URL getPhysicalUrl(URL i_vfsUrl) {
assert (i_vfsUrl != null) : "i_vfsUrl cannot be null";
logger.debug("i_vfsUrl="+ i_vfsUrl);
boolean l_isJar=true;
// Let's get the leaf archive virtual path and the components package path
String l_archiveVfsSubstring=i_vfsUrl.toString().replaceAll("^(.*\\.jar)/.*$", "$1"); // greedy search, so we wil be sure to get the full vfs path of last jar element
logger.debug("l_archiveVfsSubstring="+ l_archiveVfsSubstring);
if (i_vfsUrl.toString().equals(l_archiveVfsSubstring)) {
// if i_vfsUrl does NOT contains a jar archive, nothing to do, return the url unaltered
logger.warn("i_vfsUrl="+ i_vfsUrl+" is not a jar archive, let's see if it's a war...");
l_archiveVfsSubstring=i_vfsUrl.toString().replaceAll("^(.*\\.war)/.*$", "$1"); // greedy search, so we wil be sure to get the full vfs path of last jar element
logger.debug("l_archiveVfsSubstring="+ l_archiveVfsSubstring);
if (i_vfsUrl.toString().equals(l_archiveVfsSubstring)) {
// if i_vfsUrl does NOT contains a jar archive, nothing to do, return the url unaltered
logger.warn("i_vfsUrl="+ i_vfsUrl+" is not a war archive, returning unaltered");
return i_vfsUrl;
}
l_isJar=false;
}
String l_packagePath=i_vfsUrl.toString().substring(l_archiveVfsSubstring.length());
logger.debug("l_packagePath="+ l_packagePath);
String l_archiveName=l_archiveVfsSubstring.replaceAll("^.*/", ""); // greedy search, so we wil be sure to strip the full vfs path until the last jar element
logger.debug("l_archiveName="+ l_archiveName);
URL l_archiveVFSUrl=null;
try {
l_archiveVFSUrl = new URL(l_archiveVfsSubstring);
logger.debug("l_archiveVFSUrl="+ l_archiveVFSUrl);
}
catch (MalformedURLException l_exception) {
logger.error("Cannot obtain l_archiveVFSUrl for i_vfsUrl="+ i_vfsUrl +", l_archiveVfsSubstring="+l_archiveVfsSubstring, l_exception);
return null;
}
URLConnection l_archiveConnection=null;
try {
l_archiveConnection = l_archiveVFSUrl.openConnection();
logger.debug("l_archiveConnection="+ l_archiveConnection);
}
catch (IOException l_exception) {
logger.error("Cannot obtain l_archiveVFSUrl for i_vfsUrl="+ i_vfsUrl +", l_archiveVfsSubstring="+l_archiveVfsSubstring + ", l_archiveVFSUrl="+ l_archiveVFSUrl, l_exception);
return null;
}
Object l_archiveVirtualDir=null;
try {
l_archiveVirtualDir = l_archiveConnection.getContent();
logger.debug("l_archiveVirtualDir="+ l_archiveVirtualDir);
}
catch (IOException l_exception) {
logger.error("Cannot obtain l_archiveVFSUrl for i_vfsUrl="+ i_vfsUrl +", l_archiveVfsSubstring="+l_archiveVfsSubstring + ", l_archiveVFSUrl="+ l_archiveVFSUrl + ", l_archiveConnection="+ l_archiveConnection, l_exception);
return null;
}
// Use reflection so that we don't need JBoss in the classpath at compile time.
File l_archivePhysicalDir = (File) invokerGetter(l_archiveVirtualDir, JBOSS_VFS_310_GET_PHYSICAL_FILE_METHOD_NAME);
logger.debug("l_archivePhysicalDir="+ l_archivePhysicalDir);
String l_physicalArchiveDirUri = l_archivePhysicalDir.toURI().toString();
logger.debug("l_physicalArchiveDirUri="+ l_physicalArchiveDirUri);
if (l_isJar) {
String l_physicalArchiveUrl="jar:"+l_physicalArchiveDirUri.replaceAll("contents/$", l_archiveName)+"!"+l_packagePath;
logger.info("JAR: i_vfsUrl="+ i_vfsUrl + " -> l_physicalArchiveUrl="+ l_physicalArchiveUrl);
try {
return new URL(l_physicalArchiveUrl);
}
catch (MalformedURLException l_exception) {
logger.error("JAR: Cannot obtain l_archiveVFSUrl for i_vfsUrl="+ i_vfsUrl +", l_archiveVfsSubstring="+l_archiveVfsSubstring + ", l_archiveVFSUrl="+ l_archiveVFSUrl + ", l_archiveConnection="+ l_archiveConnection, l_exception);
return null;
}
}
// it's a war
String l_physicalArchiveUrl=l_physicalArchiveDirUri+l_packagePath;
logger.info("WAR: i_vfsUrl="+ i_vfsUrl + " -> l_physicalArchiveUrl="+ l_physicalArchiveUrl);
try {
return new URL(l_physicalArchiveUrl);
}
catch (MalformedURLException l_exception) {
logger.error("WAR: Cannot obtain l_archiveVFSUrl for i_vfsUrl="+ i_vfsUrl +", l_archiveVfsSubstring="+l_archiveVfsSubstring + ", l_archiveVFSUrl="+ l_archiveVFSUrl + ", l_archiveConnection="+ l_archiveConnection, l_exception);
return null;
}
}
/**
* Utility method to retrieve informations from an object, using reflection in a safe way
*
* @param i_target
* the object on which the method must be invoked
* @param i_getterName
* the name of the method to invoke (it must be a getter, taking no parameter)
* @return the result of the getter method, or null if there are errors
*/
private Object invokerGetter(Object i_target, String i_getterName) {
assert (i_target != null) : "Object cannot be null";
assert (i_getterName != null) : "getter cannot be null";
assert (!i_getterName.isEmpty()) : "getter cannot be empty";
Class<?> type = i_target.getClass();
Method method = null;
try {
// at first try to find the getter within public methods...
method = type.getMethod(i_getterName);
}
catch (NoSuchMethodException l_exception1) {
logger.warn("Unable to invoke getMethod for " + i_getterName + " on object " + i_target + " of class " + i_target.getClass() + ". Details: " + l_exception1 + ", trying with getDeclaredMethod");
if (logger.isDebugEnabled()) {
logger.debug("=== getMethods: available getter methods are: ===");
Method[] l_methods = type.getMethods();
for (int i = 0; i < l_methods.length; i++) {
if (l_methods[i].getName().startsWith("get")) {
logger.debug("getMethods: " + l_methods[i].toString());
}
}
}
try {
// sometimes jboss tries to hide implementation details, so let's try hacking with non public methods...
method = type.getDeclaredMethod(i_getterName);
}
catch (Throwable l_exception2) {
logger.warn("Error: unable to invoke getDeclaredMethod for " + i_getterName + " on object " + i_target + " of class " + i_target.getClass() + ". Details: " + l_exception2 + ", returning null", l_exception2);
if (logger.isDebugEnabled()) {
logger.debug("=== getDeclaredMethods: available getter methods are: ===");
Method[] l_methods = type.getDeclaredMethods();
for (int i = 0; i < l_methods.length; i++) {
if (l_methods[i].getName().startsWith("get")) {
logger.debug("getDeclaredMethod: " + l_methods[i].toString());
}
}
}
logger.error("Error: unable to invoke getMethod or getDeclaredMethod for " + i_getterName + " on object " + i_target + " of class " + i_target.getClass() + ", returning null");
return null;
}
try {
method.setAccessible(true);
}
catch (Throwable l_exception3) {
logger.error("Error: unable to invoke setAccessible for " + i_getterName + " on object " + i_target + " of class " + i_target.getClass() + ". Details: " + l_exception3 + ", returning null", l_exception3);
return null;
}
}
catch (Throwable l_exception) {
logger.error("Error: unable to invoke getMethod for " + i_getterName + " on object " + i_target + " of class " + i_target.getClass() + ". Details: " + l_exception + ", returning null", l_exception);
return null;
}
try {
return method.invoke(i_target);
}
catch (Throwable l_exception) {
logger.error("Error: unable to execute method.invoke for " + i_getterName + " on object " + i_target + " of class " + i_target.getClass() + ". Details: " + l_exception + ", returning null", l_exception);
return null;
}
}
/**
* Utility method to show contents of temporary folders. This way we can check if we are building the correct path
* for tapestry resources
*
* @param i_folderPath
* the full path of the folder whose contents must be shown
*/
private void printFolderContents(String i_folderPath) {
ArrayList<File> l_files = new ArrayList<File>();
listFolderTree(i_folderPath, l_files);
for (File l_file : l_files) {
logger.debug(l_file.getAbsolutePath());
}
}
/**
* Utility method to recursively collect all files and subfolders contained in a folder
*
* @param i_folderPath
* the full path of the folder whose contents must be examined
* @param o_foundFiles
* (output) the list of all files and subfolders contained in l_folderPath
*/
private void listFolderTree(String i_folderPath, List<File> o_foundFiles) {
File l_folder = new File(i_folderPath);
// get all the files from a directory
File[] l_filesArray = l_folder.listFiles();
for (File l_file : l_filesArray) {
if (l_file.isFile()) {
o_foundFiles.add(l_file);
}
else if (l_file.isDirectory()) {
listFolderTree(l_file.getAbsolutePath(), o_foundFiles);
}
}
}
}