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); } } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tapestry.apache.org For additional commands, e-mail: dev-h...@tapestry.apache.org