/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * WebServiceTesterServlet.java
 *
 * Created on August 6, 2004, 9:14 AM
 */

package com.sun.enterprise.webservice.monitoring;

import javax.servlet.*;
import javax.servlet.http.*;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.io.File;

import java.util.Iterator;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.net.URL;
import java.net.URLDecoder;
import java.net.MalformedURLException;

import java.util.logging.Logger;
import java.util.logging.Level;

import javax.xml.soap.SOAPMessage;
import javax.xml.namespace.QName;
import com.sun.enterprise.deployment.WebServiceEndpoint;
import com.sun.enterprise.webservice.WsUtil;
import com.sun.xml.bind.api.JAXBRIContext;
import com.sun.enterprise.webservice.ServiceInterfaceGenerator;
import com.sun.enterprise.webservice.WsUtil;

import com.sun.enterprise.util.LocalStringManagerImpl;

// JAX-WS
import javax.xml.ws.Service;
import javax.jws.WebService;
import javax.xml.ws.WebEndpoint;

// JAX-WS SPI
import com.sun.tools.ws.spi.WSToolsObjectFactory;

import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.OutputKeys;

import javax.enterprise.deploy.shared.ModuleType;

/**
 * This servlet is responsible for testing web-services.
 *
 * @author Jerome Dochez
 */
public class WebServiceTesterServlet extends HttpServlet implements MessageListener {

    private WebServiceEndpoint svcEP;
    private static Logger logger = WsUtil.getDefaultLogger();
    private static Hashtable<String, Class> gsiClasses = new Hashtable<String, Class>();
    private static Hashtable<String, Object> ports = new Hashtable<String, Object>();
    // resources...
    private static LocalStringManagerImpl localStrings =
	    new LocalStringManagerImpl(WebServiceTesterServlet.class);        
        
    public static void invoke(HttpServletRequest request, 
            HttpServletResponse response, WebServiceEndpoint endpoint) {
                    
        try {
            WebServiceTesterServlet servlet = new WebServiceTesterServlet(endpoint);            
            
            response.setCharacterEncoding("UTF-8");
            if (request.getMethod().equalsIgnoreCase("GET")) {
                servlet.doGet(request, response);
            } else {
                servlet.doPost(request, response);
            }
        } catch(Exception e) {
            try {     
                PrintWriter out = response.getWriter();
                out.print("<HTML><HEAD><TITLE>"+
                        localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.methodInvocationException",
                           "Method invocation exception")+"</TITLE></HEAD>");
                out.print("<H3>" + 
                            localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.ExceptionDetails",
                           "Exceptions details : {0}", 
                           new Object[] {e.getMessage()}) + "</H3>");                        
                out.print("<HR>");                
                e.printStackTrace(out);
                out.print("<HR>");                                
                out.print("</HTML>");
                out.close();
            } catch(Exception ex) {};            
        }
    }    
    
    
    /** Creates a new instance of WebServiceTesterServlet */
    public WebServiceTesterServlet(WebServiceEndpoint ep) {
        svcEP = ep;
    }
    
    public void doGet(HttpServletRequest req, HttpServletResponse res) 
        throws ServletException, IOException 
    {
        res.setContentType("text/html");
        res.setHeader("pragma", "no-cache");
        PrintWriter out = res.getWriter();
        String requestURL = req.getRequestURL().toString();

        Endpoint myEndpoint;
        if(svcEP.implementedByWebComponent()) {
            myEndpoint = WebServiceEngineImpl.getInstance().getEndpoint(req.getServletPath());
        } else {
            myEndpoint = WebServiceEngineImpl.getInstance().getEndpoint(req.getRequestURI());
        }
        String seiClassName = myEndpoint.getDescriptor().getServiceEndpointInterface();
        
        // For now support Tester servlet for JAXWS based services only
        try {
            Class seiClass = Thread.currentThread().getContextClassLoader().loadClass(seiClassName);
            if(seiClass.getAnnotation(javax.jws.WebService.class) == null) {
                testerNotSupportedError(myEndpoint.getDescriptor().getServiceName(), out);
                return;
            }
        } catch (ClassNotFoundException clnfEx) {
            classNotAccessibleError(Thread.currentThread().getContextClassLoader(), 
                    seiClassName, out);
            return;
        }
        
        initializePort(req);
        Class clientSEI = gsiClasses.get(requestURL);
        
        out.print("<HTML><HEAD><TITLE>"+
                            myEndpoint.getDescriptor().getServiceName().getLocalPart()+" "+
                            localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.title",
                           "Web Service Tester") + "</TITLE></HEAD>");
        out.print("<BODY><H1>"+
                            myEndpoint.getDescriptor().getServiceName().getLocalPart()+" "+
                            localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.title",
                           "Web Service Tester") + "</H1>");
        java.util.Enumeration<String> headers = req.getHeaderNames();
        
        // Microsoft Internet Explorer does not handle <BUTTON> properly
        boolean isInternetExplorer=false;
        String userAgent = req.getHeader("user-agent");
        if (userAgent!=null) {
            isInternetExplorer=userAgent.indexOf("MSIE")!=-1;
        }
        StringBuffer sb = new StringBuffer(URLDecoder.decode(requestURL));
        sb.append("?WSDL");
        
        out.print("<br>");
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.line1",
                           "This form will allow you to test your web service implementation (<A HREF=\"{0}\">WSDL File</A>)", 
                           new Object[] {sb.toString()}));
        out.print("<hr>");
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.line2",
                           "To invoke an operation, fill the method parameter(s) input boxes and click on the button labeled with the method name."));
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.methods",
                           "<H3>Methods :</H3>"));
        
        Method[] methods = clientSEI.getMethods();
        
        for (Method m : methods) {
            out.print("<FORM METHOD=\"POST\">");
            out.print(m.toString());
            out.print("<BR>");
            out.print("<INPUT TYPE=SUBMIT NAME=action value="+m.getName()+">");        
//            out.print("<BUTTON TYPE=SUBMIT TITLE=Invoke NAME=\"submit\" VALUE="+m.getName()+">" + m.getName()+"</BUTTON>");            
            out.print(" (");
            Class[] parameters = m.getParameterTypes();
            for (int i=0;i<parameters.length;i++) {
                out.print("<INPUT TYPE=TEXT NAME=PARAM"+
                        m.getName()+i+">");
                if (i!=parameters.length-1)
                    out.print(",");
            }
            out.print(")");
            out.print("<BR>");
            out.print("<HR>");
            out.print("</FORM></BODY></HTML>");
        }
        out.close();
    }
    
    public void doPost(HttpServletRequest req, HttpServletResponse res) 
        throws ServletException, IOException 
    {
        res.setContentType("text/html");
        res.setHeader("pragma", "no-cache");
        PrintWriter out = res.getWriter();
        String requestURL = req.getRequestURL().toString();
        
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.postTitle",
                           "<HTML><HEAD><TITLE>Method invocation trace</TITLE></HEAD>"));
                
        String operationName = req.getParameter("action");

        try {
            Endpoint myEndpoint;
            if(svcEP.implementedByWebComponent()) {
                myEndpoint = WebServiceEngineImpl.getInstance().getEndpoint(req.getServletPath());
            } else {
                myEndpoint = WebServiceEngineImpl.getInstance().getEndpoint(req.getRequestURI());
            }


            Class clientSEI = gsiClasses.get(requestURL);
            if (clientSEI==null) {
                initializePort(req);
                clientSEI = gsiClasses.get(requestURL);
            }
            Object port = ports.get(requestURL);
            
            // find the right method...
            Method[] methods = clientSEI.getMethods();
            Method toInvoke = null;
            for (Method m : methods) {
                if (String.valueOf(m.getName()).equals(operationName)) {
                    toInvoke = m;
                }
            }            
            
            if (toInvoke==null) {
                out.print("cannot find method " + operationName);
            } else {
                out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.methodInvocation",
                           "<H2><A> {0} </A> Method invocation</H2><BR><HR>", 
                           new Object[] {toInvoke.getName()}));
                
                // register ourselves to receive the SOAP messages...
                myEndpoint.addListener(this);

                Class[] parameterTypes = toInvoke.getParameterTypes();
                Object[] parameterValues= new Object[parameterTypes.length];
                out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.methodTrace",
                           "<h4>Method parameter(s)</h4>"));
                out.print("<table border=\"1\">");
                out.print("<tr>");
                out.print("<th>Type</th>");
                out.print("<th>Value</th>");
                out.print("</tr>");
                for (int i=0;i<parameterTypes.length;i++) {
                    out.print("<tr>");
                    String webValue = req.getParameter("PARAM"+
                            toInvoke.getName()+i);
                    out.print("<td>" + parameterTypes[i].getName()+"</td>");
                    out.print("<td>" + webValue + "</td>");
                    parameterValues[i] = convertWebParam(parameterTypes[i],webValue);
                    out.print("</tr>");
                }
                out.print("</table>");
                out.print("<HR>");
                out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.methodReturn",
                           "<h4>Method returned</h4>")
                           +toInvoke.getReturnType().getName() + " : \"<b>" 
                        + toInvoke.invoke(port, parameterValues)+"</b>\"");
                out.print("<HR>");
                if (request!=null) {
                    // let's print the SOAP request
                    out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.soapReq",
                           "<h4>SOAP Request</h4>"));
                    dumpMessage(request, out);                    
                }
                if (toInvoke.getAnnotation(javax.jws.Oneway.class) == null &&
                        response!=null) {
                    // let's print the SOAP request
                    out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.soapResp",
                           "<h4>SOAP Response</h4>"));
                    dumpMessage(response, out);                    
                }
                myEndpoint.removeListener(this);
            } 
        
        } catch(Throwable e) {
            out.print(localStrings.getLocalString(
                       "enterprise.webservice.serviceExceptionError",
                       "<H2>Service invocation threw an exception with message : {0}; Refer to the server log for more details</H2><BR><HR>", 
                       new Object[] {e.getMessage()}));
            throw new ServletException(e);
        } 
               
        out.print("</HTML>");
        out.close();
    }
    
    private void dumpMessage(MessageTrace message, PrintWriter out) throws Exception {
                            
        /*String xsl = "<?xml version=\"1.0\"?>"+
        "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">"+
        "<xsl:template match=\"/\">" +
        "<xsl:for-each select=\"*\">" +
        "&lt;<xsl:value-of select=\".\"/>&gt;" +
        "</xsl:for-each>" +
        "</xsl:template>" +
        "</xsl:stylesheet>";*/
        
        // now transform it...
        ByteArrayInputStream bais = new ByteArrayInputStream(message.getMessage(true).getBytes());
        StreamSource ss = new StreamSource(bais);
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamResult sr = new StreamResult(baos);
        
        TransformerFactory factory =  TransformerFactory.newInstance();        
        Transformer transformer = factory.newTransformer();
        
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.transform(ss, sr);
        
        
        out.print("<HR><blockquote><pre xml:lang>");
        String value = baos.toString();
        value = value.replaceAll("<", "&lt;");
        value = value.replaceAll(">", "&gt;");
        out.write(value);
        
        out.print("</pre></blockquote><HR>");
        /*
        baos = new ByteArrayOutputStream();
        sr = new StreamResult(baos);
        bais.reset();
        transformer.transform(ss, sr);
        StringReader reader = new StringReader(xsl);        
        transformer = factory.newTransformer(new StreamSource(reader));
        out.write(baos.toString());
        */        
    }    
    
    private MessageTrace request = null;
    private MessageTrace response = null;
    
    public void invocationProcessed(MessageTrace request, MessageTrace response) {
        this.request = request;
        this.response = response;
    }
        
    private Object convertWebParam(Class targetParamType, String webValue) {
        
        Object convertedValue = null;
        if (webValue==null || webValue.length()==0) {
            return null;
        }
        if (String.class.equals(targetParamType)) {
             convertedValue = webValue;
        } else {
            try {
                if (int.class.equals(targetParamType) || 
                        Integer.class.equals(targetParamType)) {
                    convertedValue = new Integer(webValue);
                }
                if (boolean.class.equals(targetParamType) ||
                        Boolean.class.equals(targetParamType)) {
                    convertedValue = new Boolean(webValue);
                }
                if (char.class.equals(targetParamType) || 
                        (Character.class.equals(targetParamType))) {
                    convertedValue = new Character(webValue.charAt(0));
                }                
                
                if (long.class.equals(targetParamType) || 
                        (Long.class.equals(targetParamType))) {
                    convertedValue = new Long(webValue);
                }
                if (float.class.equals(targetParamType) || 
                        (Float.class.equals(targetParamType))) {
                    convertedValue = new Float(webValue);
                }  
                if (double.class.equals(targetParamType) || 
                        (Double.class.equals(targetParamType))) {
                    convertedValue = new Double(webValue);
                }  
                if (byte.class.equals(targetParamType) || 
                        (Byte.class.equals(targetParamType))) {
                    convertedValue = new Byte(webValue);
                }   
                if (short.class.equals(targetParamType) || 
                        (Short.class.equals(targetParamType))) {
                    convertedValue = new Short(webValue);
                }     
                if (StringBuffer.class.equals(targetParamType)) {
                    convertedValue = new StringBuffer(webValue);
                }                   
            } catch(NumberFormatException nfe) {
                System.out.println("Cannot convert " + webValue + " in " + targetParamType);
            }
        } 
        return convertedValue;
    }
    
    private void classNotAccessibleError(ClassLoader cl, String className, PrintWriter out) {
        
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.CNFTitle",
                           "<HTML><HEAD><TITLE>Method invocation exception</TITLE></HEAD>"));
        if (cl==null) {
            out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.CNFServerError",
                           "<H3>Internal server error, debugging is not available</H3>"));
        } else {
            out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.CNFerror2",
                           "<H3>Cannot load class {0} - Verify class presence in bundle</H3>", 
                           new Object[] {className}));
        }
        out.print("<HR>");
        out.print("</HTML>");
        out.close();          
    }
    
    private void testerNotSupportedError(QName svcName, PrintWriter out) {
        
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.TesterNSTitle",
                           "<HTML><HEAD><TITLE>Tester feature not supported</TITLE></HEAD>"));
        out.print("<BODY>");
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.TesterNSerror2",
                           "Service {0} looks like a JAXRPC based webservice.", 
                           new Object[] {svcName}));
        out.print("<br><br>");
        out.print(localStrings.getLocalString(
		    	   "enterprise.webservice.monitoring.TesterNSdetail",
                           "Please note that the tester feature is supported for JAXWS based webservices only"));
        out.print("</BODY>");
        out.print("</HTML>");
        out.close();          
    }

    private void initializePort(HttpServletRequest req) 
        throws ServletException, IOException {

        String requestURL = req.getRequestURL().toString();
        Endpoint myEndpoint;
        if(svcEP.implementedByWebComponent()) {
            myEndpoint = WebServiceEngineImpl.getInstance().getEndpoint(req.getServletPath());
        } else {
            myEndpoint = WebServiceEngineImpl.getInstance().getEndpoint(req.getRequestURI());
        }
        
        // get our service qname
        QName serviceName =
                new QName(myEndpoint.getDescriptor().getWsdlPort().getNamespaceURI(),
                myEndpoint.getDescriptor().getWebService().getName());
        
        // construct the WSDL http url
        StringBuffer sb = new StringBuffer(URLDecoder.decode(requestURL));
        sb.append("?WSDL");
        
        URL[] urls = new URL[1];
        String classesDir;
        try {
            URL wsdlUrl = new URL(sb.toString());
            // create client artifacts
            classesDir = wsImport(wsdlUrl);
            urls[0] = (new File(classesDir)).toURL();
        } catch(MalformedURLException mue) {
            throw new ServletException(mue);
        }
        
        // we need a class loader to load the just created client artifacts. Save the current classloader
        ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
        // we now create a new classloader using the parent class loader
        // which should be the shared application classloader. I don't want
        // the application class loader to not get into client/server
        // classes clashes.
        // the immediate classloader is the WebApp classloader, its parent is the 
        // application classloader, we want the parent of that one
        ClassLoader testerCL = new URLClassLoader(urls, currentLoader.getParent().getParent());
        try {
            Thread.currentThread().setContextClassLoader(testerCL);
            String serviceClassName = getServiceClass(
                    JAXBRIContext.mangleNameToClassName(serviceName.getLocalPart()),
                    classesDir);
            if (serviceClassName==null) {
                throw new RuntimeException("Service Class not generated as expected");
            }
            
            Class serviceClass = testerCL.loadClass(serviceClassName);
            Service service = Service.create(new URL(sb.toString()), serviceName);            
            if (service==null) {
                throw new RuntimeException("Cannot load Service");
            }
            
            // find the right port... for this look at the @WebService annotation in SEI and get the portName
            String portClassName = getPortClass(myEndpoint, serviceClass);
            if (portClassName==null) {
                throw new RuntimeException("Cannot find the correct port class.");
            }            
            
            Class portClass = testerCL.loadClass(portClassName);
            Object port = service.getPort(myEndpoint.getDescriptor().getWsdlPort(), portClass);
            if (port==null) {
                throw new RuntimeException("Cannot find the correct port class.");
            }            
            if (port!=null) {
                ports.put(requestURL, port);
                gsiClasses.put(requestURL, portClass);
            }
        } catch(Exception e) {
            throw new ServletException(e);
        } finally {
            // restore the class loader
            Thread.currentThread().setContextClassLoader(currentLoader);
            // delete client artifacts, everything should be loaded or it failed
            deleteDir(new File(classesDir));
        }
    }
    
    private String wsImport(URL wsdlLocation) throws IOException {
            
        File classesDir = new File(System.getProperty("java.io.tmpdir"));
        // create a dumy file to have a unique temporary name for a directory
        classesDir = File.createTempFile("jax-ws", "tester", classesDir);
        classesDir.delete();
        classesDir.mkdirs();
        
        String[] wsimportArgs = new String[4];
        wsimportArgs[0]="-d";
        wsimportArgs[1]=classesDir.getAbsolutePath();
        wsimportArgs[2]="-keep";
        wsimportArgs[3]=wsdlLocation.toExternalForm();
        WSToolsObjectFactory tools = WSToolsObjectFactory.newInstance();
        boolean success = tools.wsimport(System.out, wsimportArgs);
        if (logger!=null) {
            logger.log(Level.INFO, "Invoking wsimport with " + wsdlLocation);
        } else {
            System.out.println("Invoking wsimport with " + wsdlLocation);
        }
        
        if (success) {
            if (logger!=null)
                logger.log(Level.INFO, "wsimport successful");
        } else {
            if (logger!=null)
                logger.log(Level.SEVERE, "wsimport failed");
            return null;
        }
        return classesDir.getAbsolutePath();
    }
    
    private String getServiceClass(String serviceClassName, String classesDirPath) {
        //wsimport generated Service Class always is serviceQName.getLocalPart()+".class"
        // So just look for this class and return that class
        File classesDir = new File(classesDirPath);
        if (!classesDir.exists()) {
            return null;            
        }
        File[] classes = getListOfFiles(classesDir).toArray(new File[0]);
        String resolvedServiceClass = null;
        String svcClass = null;
        for (File f : classes) {            
            if (f.getName().endsWith(serviceClassName+"_Service.class")){
                resolvedServiceClass = f.getAbsolutePath().substring(classesDirPath.length()+1);
                
            } else {
                if (f.getName().endsWith(serviceClassName+".class")){
                    svcClass = f.getAbsolutePath().substring(classesDirPath.length()+1);
                  
                }
            }         
        }
        //Incase there is a clash JAXWS resolves the serviceClass
        //to serviceName_Service.class Use the first if it is present
        //Fix for issue 3403
        if ( resolvedServiceClass != null){
            svcClass = resolvedServiceClass;
        }    
        if (svcClass != null) {
            svcClass = svcClass.substring(0, svcClass.indexOf(".class"));
            return svcClass.replaceAll("\\"+File.separator,".");
        } else {
            return null;        
        }
    }

    private String getPortClass(Endpoint ep, Class serviceClass)
                                            throws Exception {
        String pkg = serviceClass.getPackage().getName();
        for(Method m : serviceClass.getMethods()) {
            WebEndpoint webEP = (WebEndpoint) 
                m.getAnnotation(WebEndpoint.class);
            if(webEP == null || webEP.name() == null ||
                    webEP.name().length() == 0) {
                continue;
            }
            String getPortMethodName = "get" + 
                    JAXBRIContext.mangleNameToClassName(webEP.name());
            Method getPortMethod = 
                    serviceClass.getMethod(getPortMethodName, (Class[])null);
            return getPortMethod.getReturnType().getName();
        }
        return null;
    }
    
    private List<File> getListOfFiles(File path) {
        
        File[] files = path.listFiles();
        List<File> result = new ArrayList<File>();
        for (File f : files) {
            if (f.isDirectory()) {
                result.addAll(getListOfFiles(f));
            } else {
                result.add(f);                
            }
        }
        return result;
    }
    
    private void deleteDir(File path) {

        File[] files = path.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                deleteDir(f);
            }
            f.delete();
        }
        path.delete();
    }    
}
