1

I have a tree (tree2 tomahawk 1.1.11) that shows a list of dirs and files. When I click on a file I would like to show the download dialog to let the client download the file. My page looks like

    ...
    <h:form>
        <t:tree2 id="tree" value="#{listFiles.treeData}"
                 var="node" varNodeToggler="t" >
            <f:facet name="folder">
                <h:panelGroup>
                    <f:facet name="expand">
                        <t:graphicImage value="images/folderOpen.png"
                                        rendered="#{t.nodeExpanded}}"
                                        border="0" />
                    </f:facet>
                    <f:facet name="collapse">
                        <t:graphicImage value="images/folderClose.png"
                                        rendere="#{t.nodeExpanded}}"
                                        border="0" />
                    </f:facet>
                    <h:outputText value="#{node.description}"
                                  styleClass="nodeFolder" />
                </h:panelGroup>
            </f:facet>
            <f:facet name="file">
                <h:panelGroup>
                    <h:commandLink action="#{listFiles.download()}" >
                        <t:graphicImage value="images/file.png" border="0" />
                        <h:outputText value="#{node.description}" />
                    </h:commandLink>
                </h:panelGroup>
            </f:facet>
        </t:tree2>
    </h:form>
    ...

And my bean is

@ManagedBean
@RequestScoped
public class ListFiles implements Serializable {

    private String path = "C:\\";
    private TreeNode treeRoot;
    private File dirRoot;
    @ManagedProperty("#{userVerifier}")
    private UserVerifier userVerifier;

    public void setUserVerifier(UserVerifier userVerifier) {
        this.userVerifier = userVerifier;
    }

    public UserVerifier getUserVerifier() {
        return userVerifier;
    }

    public TreeNode getTreeData() {
        path = loadConfiguredPath();
        String dependencia = userVerifier.getDependencia();

        if (dependencia.equals("DESARROLLO")) {
            path = path + "dataFiles";
            treeRoot = new TreeNodeBase("folder", "SRC", false);
        } else {
            path = path + "dataFiles\\" + dependencia;
            treeRoot = new TreeNodeBase("folder", dependencia, false);
        }

        dirRoot = new File(path);
        createTree(dirRoot, treeRoot);

        return treeRoot;
    }

    private void createTree(File fileRoot, TreeNode treeRoot) {
        File[] files = fileRoot.listFiles();
        TreeNodeBase tnb;
        for (File f : files) {
            if (f.isDirectory()) {
                tnb = new TreeNodeBase("folder", f.getName(), false);
                treeRoot.getChildren().add(tnb);
                createTree(f, tnb);
            }
            if (f.isFile()) {
                tnb = new TreeNodeBase("file", f.getName(), false);
                treeRoot.getChildren().add(tnb);
            }
        }
        return;
    }

    private String loadConfiguredPath() {
        String dir;
        ReadXML reader = new ReadXML(".\\webapps\\SRC\\configFiles\\confSRC.xml");
        dir = reader.getValue("baseDir");
        if (dir == null) {
            return path;
        } else {
            return dir;
        }
    }

    public String download(){
        System.out.println("Yes we are downloading");
        return "ok";
    }
}

Everything works fine except I don't know how to achieve the download action when click on the h:commandLink

The only type of files I have are txt or csv.

UPDATE:

Now as I have the code, this exception is thrown.

javax.servlet.ServletException
    javax.faces.webapp.FacesServlet.service(FacesServlet.java:422)
    org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:349)

UPDATE:

I'll post my web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>

    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>

    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>

    <filter>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>

        <init-param>
            <param-name>uploadMaxFileSize</param-name>
            <param-value>5g</param-value>
        </init-param>
        <init-param>
            <param-name>uploadThresholdSize</param-name>
            <param-value>500m</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>

    <filter-mapping>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <servlet-name>/faces/*</servlet-name>
    </filter-mapping>

    <context-param>
        <param-name>org.apache.myfaces.CHECK_EXTENSIONS_FILTER</param-name>
        <param-value>false</param-value>
    </context-param>

</web-app>
BRabbit27
  • 6,333
  • 17
  • 90
  • 161

1 Answers1

1

Basically, you'd need to pass the physical File or at least File#getAbsolutePath() around as value so that the download action method can read it from disk. I've never used <t:tree2> so I checked the Javadoc of TreeNodeBase and it doesn't seem to support anything else than String description as node value. It's not possible to set it with a File. So you really need to pass File#getAbsolutePath() down into it. I think you can use the String identifier argument for this:

tnb = new TreeNodeBase("file", f.getName(), f.getAbsolutePath(), false);

Then, in the view, just pass it to the action method:

<h:commandLink action="#{listFiles.download(node.identifier)}" >

Finally, stream it as follows:

public String download(String absolutePath) throws IOException {
    File file = new File(absolutePath);
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();

    externalContext.setResponseHeader("Content-Type", externalContext.getMimeType(file.getName()));
    externalContext.setResponseHeader("Content-Length", String.valueOf(file.length()));
    externalContext.setResponseHeader("Content-Disposition", "attachment;filename=\"" + file.getName() + "\"");

    InputStream input = null;
    OutputStream output = null;;

    try {
        input = new FileInputStream(file);
        output = externalContext.getResponseOutputStream();
        IOUtils.copy(input, output);
    } finally {
        IOUtils.closeQuietly(output);
        IOUtils.closeQuietly(input);
    }

    facesContext.responseComplete();
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • `javax.servlet.ServletException javax.faces.webapp.FacesServlet.service(FacesServlet.java:422) org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:349)` still having this exception. I think it has to do with the ExtensionFilter configuration on web.xml – BRabbit27 Nov 08 '11 at 20:44
  • This is unlikely. What's the root cause? Look further down into the trace. – BalusC Nov 08 '11 at 20:47
  • java.lang.NullPointerException javax.faces.component.UIComponentBase$FacetsMap.put(UIComponentBase.java:2923) javax.faces.component.UIComponentBase$FacetsMap.put(UIComponentBase.java:2895) com.sun.faces.application.view.StateManagementStrategyImpl$4.visit(StateManagementStrategyImpl.java:344) com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151) javax.faces.component.UIComponent.visitTree(UIComponent.java:1589) javax.faces.component.UIComponent.visitTree(UIComponent.java:1600) javax.faces.component.UIForm.visitTree(UIForm.java:344) – BRabbit27 Nov 08 '11 at 20:50
  • 1
    Ick, try adding context param of `javax.faces.PARTIAL_STATE_SAVING` with value of `false` to `web.xml` and retry. Can you tell what Mojarra impl/version you're using? – BalusC Nov 08 '11 at 20:56
  • 1
    Oh wait, you're creating the tree data in the getter method. You should do it in a `@PostConstruct` method instead. The getter should only return the property. – BalusC Nov 08 '11 at 20:57
  • I'm using JSF 2.1 (I think that's Mojarra version isn't it?)... for the code it should read as: `public TreeNode getTreeData(){return treeRoot;} @PostConstruct public void init(){ //Code for creating the tree }` – BRabbit27 Nov 08 '11 at 22:41
  • Adding the context param **java.faces.PARTIAL_STATE_SAVING** made it work. I also change the `@PostConstruct` and the `getter`. I read about the PARTIAL_STATE_SAVING but I cannot figure out how it is related to the downloading problem. – BRabbit27 Nov 08 '11 at 23:48
  • 1
    The exact JSF impl/version should be visible in webapp startup log, e.g. Mojarra 2.1.3. According to the stacktrace you're definitely using Mojarra, I only need to know the exact version. As to the solution, the NullPointerException which you got there suggests that the partial state saving of a `` tag has failed. Right now I can't tell if it's a bug in Mojarra or a bug in Tomahawk's t:tree2. You could try with latest Mojarra which is 2.1.3 or try it together with the alternative JSF implementation MyFaces. This problem has nothing to do with your own code. – BalusC Nov 09 '11 at 00:09
  • Well, I'm using Tomcat 7.0, I really don't know what Mojarra version I'm currently using. In my webapps directory there's no *startup log*. But in the *logs* directory it says something about *Catalina*. As for the Partial State Saving, when I put it **false** it will not save any state and always load everything when the view is shown, is that true? Thanks, I really appreciate your help. You are a master of JSF hehe. – BRabbit27 Nov 09 '11 at 02:25
  • 1
    Tomcat doesn't ship with JSF out the box, so you should surely have downloaded and installed it yourself in `/WEB-INF/lib` folder. Do you know what download link you've used? Perhaps you've still the original zip file? It should contain the exact version in the filename. The webapp startup logs can be found in Tomcat/logs (or just in console of your IDE). Disabling the partial state saving has the only disadvantage that the size of the view state grows in memory and that processing ajax requests will be somewhat slower. – BalusC Nov 09 '11 at 02:28
  • I get it! It's just that I use Netbeans and it has already the library JSF, and doing some research and reading what you tell me I currently have Mojarra 2.1.1. I'll try and use the latest stable version. BTW, if I'm using Tomahawk wouldn't be better to use MyFaces? is there an equivalent of Tomahawk in Mojarra project?. And for the other issue, I'm not using (I've never used) Ajax, but I'll give it a try and try to investigate about this STATE_SAVING thing. THanks – BRabbit27 Nov 09 '11 at 15:06
  • 1
    No, Tomahawk is a standalone component library which just happen to be developed/maintained by the same guys as those behind MyFaces. Tomahawk should run without problems on all other JSF implementations, including Mojarra. Mojarra doesn't have an extended component library, but the Mojarra developers have a "sandbox" which is known as [Scales](http://kenai.com/projects/scales). It doesn't contain much of really interesting components though. – BalusC Nov 09 '11 at 15:08
  • I'm back with a little question. When I'm creating the tree of files, it takes much time to display it. My root folder has at most 5 files an takes about 1 o 2 minutes. How can I make it much faster? Is it because of the recursion used to create the tree? Cheers. – BRabbit27 Nov 10 '11 at 15:47
  • That's indeed way too long. What part took the longest? Run a debugger or add poor man's sysout/loggers printing the current/elapsed time. – BalusC Nov 10 '11 at 15:58
  • Well, I did the timing but it seems that the bean is doing the creation of the tree fast enough. Less than a sec that's what the times say. Maybe some rendering issue? How can I check that? – BRabbit27 Nov 10 '11 at 17:36
  • BTW forgot to say that, after first time (when it takes time) the second and third times it takes "nothing" to render the tree. – BRabbit27 Nov 10 '11 at 17:52