Schlagwörter: Richfaces Kommentarverlauf ein-/ausschalten | Tastaturkürzel

  • Andreas Höhmann 14:43 am Monday, 31. March 2008 Permalink |
    Tags: , , File Download, , MyFaces, Richfaces   

    JSF, Ajax and file download 

    Today I show you a example which combines Ajax (Richfaces) and a „normal“ download in a JSF application.

    Here is the bean with the download-code:

    import java.io.File;
    import java.io.IOException;
    import javax.faces.context.FacesContext;
    import javax.faces.event.ActionEvent;
    import javax.servlet.http.HttpServletResponse;
    
    public class DownloadBean {
    
      // actionListener="#{bean.execute}"
      public void execute (ActionEvent event) {
       download();
      }
    
      // action="#{bean.download}"
      public String download() {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        // XXX create temp. filecontent ... resultFile
        writeOutContent(getServletResponse(), new File(this.resultFile), "file.xml");
        facesContext.responseComplete();
        // dont use jsf-navigation-rules
        return null;
      }
    
      void writeOutContent(final HttpServletResponse res, final File content, final String theFilename) {
        if (content == null)
          return;
        try {
          res.setHeader("Pragma", "no-cache");
          res.setDateHeader("Expires", 0);
          res.setContentType("text/xml");
          res.setHeader("Content-disposition", "attachment; filename=" + theFilename);
          fastChannelCopy(Channels.newChannel(new FileInputStream(content)), Channels.newChannel(res.getOutputStream()));
        } catch (final IOException e) {
          // TODO produce a error message 🙂
        }
      }
    
      void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
        final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
        while (src.read(buffer) != -1) {
          buffer.flip();
          dest.write(buffer);
          buffer.compact();
        }
        buffer.flip();
        while (buffer.hasRemaining()){
          dest.write(buffer);
        }
      }
    }
    

    Now the facelets snipplets: Not Working – Javascript Error 😦

    <h:form>
     <h:commandLink actionListener="#{command.execute}" value="Download"/>
     <h:commandLink action="#{command.download}" value="Download"/>
    </h:form>
    

    Not Working – Download is shown in the current page – the ajax page will replaced with the download-content 😦

    <a4j:form>
      <a4j:commandLink actionListener="#{command.execute}" value="Download"/>
      <a4j:commandLink action="#{command.download}" value="Download"/>
    </a4j:form>
    

    Working 🙂

    <a4j:form>
     <a4j:htmlCommandLink actionListener="#{command.execute}" value="Download"/>
     <a4j:htmlCommandLink action="#{command.download}" value="Download"/>
    </a4j:form>
    
    <h:form>
     <a4j:htmlCommandLink actionListener="#{command.execute}" value="Download"/>
     <a4j:htmlCommandLink action="#{command.download}" value="Download"/>
    </h:form>
    

    The a4j:htmlCommandLink was made for this szenario.

    Notice: the fast channel copy routine was introduced by waffel.

     
    • walthersoft 13:51 am Freitag, 11. Juli 2008 Permalink | Zum Antworten anmelden

      Nice, was looking for something like this. However, it doesn’t compile, where do you get ‚getServletResponse()‘ in your download() method from ?

    • Andreas Höhmann 7:48 am Donnerstag, 31. Juli 2008 Permalink | Zum Antworten anmelden

      Its a private method wich return the current HttpServletResponse, i haven’t posted because the code is very easy

    • Dummal 15:38 am Mittwoch, 23. Dezember 2009 Permalink | Zum Antworten anmelden

      You saved my day… man!

      thanx a bunchh!

    • ajayjiit 13:42 am Donnerstag, 8. Juli 2010 Permalink | Zum Antworten anmelden

      Hi can you list down the which packages classes ti be imported and zars required.
      iam not able to run it

  • Andreas Höhmann 11:09 am Friday, 7. March 2008 Permalink |
    Tags: a4j, Apache, , Reverse Proxy, Richfaces, View Handler   

    JSF behind reverse proxy 

    The problem „try to run a jsf application behind a reverse proxy“ it will not work if you change the context-path.

    Imagine a local deployed jsf application „hello-world.war“. You can start browsing at http://localhost:8080/hello-word/. The browser handles the absolute paths for css and javascript-libs correctly and all forms will work. Remeber that all resource-urls converted to a „contex-absolut-url“ by the jsf-framework.

    If you open http://localhost:8080/hello-world/index.jsf the the client html will contains resources like this:

    <link rel=’stylesheet‘ type=‚text/css‘
    href
    =‘/hello-world/a4j_3_1_3.GAcss/tabPanel.xcss/DATB/eAGTWzQ.BgAD.AG8.jsf‘>
    <script type=‚text/javascript‘ src=‚/hello-world/a4j_3_1_3.GAorg.ajax4jsf.javascript.PrototypeScript.jsf‘>
    <form id=„j_id14“ name=„j_id14“ method=„post“ action=„/hello-world/index.jsf“>

    (Note: I use richfaces 3.1.4 in the example)

    The browser will append each absolute url (starting with ‚/‘) to the host to make the request. And here is the problem if you want to use this jsf-application behind a reverse proxy. Then all absolute URLs pointing to a wrong resource and nothing will work. To try this you can install a local apache web server the following to the httpd.conf:

    Listen 9090
    ProxyRequests On
    ProxyPreserveHost On
    ProxyVia full
    ProxyPass /external-path/hello-world http://localhost:8080/hello-world/

    (This will start the apache at localhost:9090 to avoid port-conflicts)

    If you open http://localhost:9090/external-path/hello-world/index.jsf the the client html contains the same absolute urls and the browser try to load these resource from the „wrong server“: http://localhost:9090/hello-world/. If you imagine a real world szenario then the reverse proxy will use a „real world domain“ and the urls looks like this http://www.mycompany.de/external-path/hello-world/.

    So whats the solution for this? I try my own ViewHandler to convert absolute to relative urls. Two things must be done:

    1. Implement the ViewHandler

    2. Register the ViewHandler

    First the code … we extend javax.faces.application.ViewHandler and overwrite the two methods getActionURL and getResourceURL.

    package de.ahoehma.jsf;
    
    public class ReverseProxyViewHandler extends ViewHandler {
    
     @Override
     public String getActionURL(final FacesContext context, final String viewId) {
      return getRelativeURL(context, this.defaultHandler.getActionURL(context, viewId));
     }
    
     @Override
     public String getResourceURL(final FacesContext context, final String path) {
      return getRelativeURL(context, this.defaultHandler.getResourceURL(context, path));
     }
    
     /**
      * Transform the given URL to a relative URL <b>in the context of the current
      * faces request</b>. If the given URL is not absolute do nothing and return
      * the given url. The returned relative URL is "equal" to the original url but
      * will not start with a '/'. So the browser can request the "same" resource
      * but in a relative way and this is important behind reverse proxies!
      *
      * @param context
      * @param theURL
      * @return
      */
     private String getRelativeURL(final FacesContext context, final String theURL) {
      final HttpServletRequest request = ((HttpServletRequest) context.getExternalContext().getRequest());
      String result = theURL;
      if (theURL.startsWith("/")) {
       int subpath = StringUtils.countMatches(getPath(request), "/") - 1;
       String pathPrefix = "";
       if (subpath > 0) {
        while (subpath > 0) {
         pathPrefix += "/..";
         subpath--;
        }
        pathPrefix = StringUtils.removeStart(pathPrefix, "/");
       }
       result = pathPrefix + result;
      }
      return result;
     }
    
     /**
      * Get the url-path from the given request.
      *
      * @param request
      * @return clean path
      */
     private String getPath(final HttpServletRequest request) {
      try {
       // TODO handle more than two '/'
       return StringUtils.replace(new URI(request.getRequestURI()).getPath(), "//", "/");
      } catch (final URISyntaxException e) {
      // XXX URISyntaxException ignored
      return StringUtils.EMPTY;
      }
     }
    }

    The absolute2relative-algorithm prepends for each „/“ in the current request-url a „../“ to the resource/action-url … thats all 😀

    And last register the view-handler. If you use A4J you have to add this to your web.xml:

    <context-param>
    <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
    <param-value>de.ahoehma.jsf.ReverseProxyViewHandler</param-value>
    </context-param>

    That’s all 😀

    Now if you open http://localhost:9090/external-path/hello-world/index.jsf the client html contains only relative urls:

    <link rel=’stylesheet‘ type=‚text/css‘
    href
    =‘../hello-world/a4j_3_1_3.GAcss/tabPanel.xcss/DATB/eAGTWzQ.BgAD.AG8.jsf‘>
    <script type=‚text/javascript‘ src=‚../hello-world/a4j_3_1_3.GAorg.ajax4jsf.javascript.PrototypeScript.jsf‘>
    <form id=„j_id14“ name=„j_id14“ method=„post“ action=„../hello-world/index.jsf“>

     
  • Andreas Höhmann 19:59 am Thursday, 17. January 2008 Permalink |
    Tags: , , File Upload, , Richfaces,   

    Fileupload mit Richfaces 

    This article shows the usage of a tomahawk-fileupload with richfaces and facelets. The result is a upload-formular where the upload-button is disabled until the user select a file. All this can be done without any line self hacked javascript-code 🙂

    The upload-button depends on a bean-property ‚uploadedFilename‘ which is sync via AJAX.

    So it is possible to enabled the upload-button before the file is really uploaded to the server.
    We have a jsf-bean:

    public class FileUploadBean  {
    
      private UploadedFile uploadedFile;
      private String uploadedFilename;
    
      // ... setter and getter ...
    
      public void uploadFile(final ActionEvent event) {
        if (null != this.uploadedFile) {
          LOG.debug("file-size '"+this.uploadedFile.getSize()+"'");
        }
      }
    }

    and the facelets-page:

    <ui:component
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:t="http://myfaces.apache.org/tomahawk"
      xmlns:a4j="http://richfaces.org/a4j">
    
    <c:set var="controlId" value="controls" />
    
    <a4j:form>
      <a4j:jsFunction name="updateFilename" reRender="#{controlId}">
       <a4j:actionparam name="filename" assignTo="#{FileUploadBean.uploadedFilename}" />
      </a4j:jsFunction>
    </a4j:form>
    
    <h:form enctype="multipart/form-data">
     <t:inputFileUpload id="fileupload"
                        size="40"
                        value="#{FileUploadBean.uploadedFile}"
                        storage="file"
                        required="true"
                        onchange="updateFilename(this.value)"/>
    
     <h:panelGroup id="#{controlId}">
       <c:choose>
         <c:when test="#{!empty FileUploadBean.uploadedFilename}">
           <!-- User has selected a File - enable Upload-Control -->
           <h:commandLink value="Upload enabled ..."
                          actionListener="#{FileUploadBean.uploadFile}"/>
         </c:when>
         <c:otherwise>
           <!-- User doenst have selected a File - DISABLE Upload-Control -->
           <h:outputText value="Upload disabled"/>
         </c:otherwise>
       </c:choose>
     </h:panelGroup>
    </h:form>
    
    </ui:component>
     
c
Neuen Beitrag erstellen
j
nächster Beitrag/nächster Kommentar
k
vorheriger Beitrag/vorheriger Kommentar
r
Antworten
e
Bearbeiten
o
zeige/verstecke Kommentare
t
Zum Anfang gehen
l
zum Login
h
Zeige/Verberge Hilfe
Shift + ESC
Abbrechen