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

Reply via email to