Aktualisierungen April, 2009 Kommentarverlauf ein-/ausschalten | Tastaturkürzel

  • Andreas Höhmann 12:30 am Monday, 27. April 2009 Permalink |
    Tags: , Example, , , Realworld,   

    Realworld JSF Application Story 

    Vielleicht hatte ich es in einem früheren Posting schonmal erwähnt, ich arbeite momentan für einen großen dt. Firma an einem Web 2.0 Projekt und das ist jetzt fertig 😀

    Ich konnte dabei all die wunderbaren „neuen“ Technologien einsetzen, u.a. JSF und damit eine funktionsfähige (also den Anforderungen des Kunden voll entsprechende) und zugleich performante „Web 2.0“ Applikation bauen.

    Die Applikation nimmt sich dem Thema „Sicherheitstechnik“ (für die Auskenner: ISO 13849-1 und IEC 62061) an.

    Mal kurz zu den verwendeten „Technologien/Frameworks/Tools/Ideen“:

    • Daten ziehen wir aus einer SAP Knowledgebase und aus einer Datenbank via OpenJPA
    • Services/Manager sind „normale“ Javaklassen (Stichwort Design-Pattern),
      als Kleber verwenden wir Spring (Stichwort Dependency Injection)
    • UI mit JBoss Richfaces (Stichwort Ajax), JSF + Facelets (Stichwort Xhtml-Template), jQuery, CSS, etc.
    • Buildsystem, Projektseiten mit Maven2
    • Continues Integration via TeamCity und Maven2
    • Tests hauptsächlich mit TestNG aber teilweise auch mit JUnit
    • Laden/Speichern von Benutzerdaten via XStream (FileupLoad via Tomahawk), das alles noch Versionskompatibel, d.h. selbsgestrickte XML-Transformationskette für DomainModell-Updates
    • PDF Reportgenerierung mit iText
    • Anbindung an ein „Single Sign On“ System via Spring Security, Webservices

    Im Rückblick kann ich sagen, dass eigentlich alles ohne größere Probleme miteinander funktioniert hat
    … so soll es sein 😀

     
  • Andreas Höhmann 18:04 am Wednesday, 4. March 2009 Permalink |
    Tags:   

    Show Richfaces Version 

    Richfaces provide his version in the el-variable a4j.

    So put a #{a4j} in a facelets-template and you will see a version-string like this

    Jboss Richfaces by richfaces.org, version v.3.3.0.GA SVN $Revision: 12244 $ $Date: 2009-01-13 05:40:53 +0200 $

     
  • Andreas Höhmann 23:12 am Tuesday, 9. September 2008 Permalink |
    Tags: Adapter, Node, , Select,   

    Richfaces internal Tree Node Selection Howto 

    Waffel beschreibt in seinem neuesten Blogbeitrag wie man den Richfaces Tree auch aus dem System heraus ansprechen kann um eine bestimmte Node zu selektieren und sichtbar zu machen. So auf anhieb findet sich bisher keine vernünftige Dokumentation zu dem Thema.

    Er kombiniert in seinem Beispiel den Tree mit einem TreeNodeAdapter (um das lästige Erstellen der Nodeklassen zu überspringen) und benutzt schließlich einen TreeWalker um die gewünschte Node zu finden und aufzuklappen.

    Vielleicht wird in einer der nächsten RF-Versionen das Handling mit dem Baum ja noch einfacher. Aus Benutzersicht (UI) läßt der Baum ja kaum noch Wünsche offen. Serverseitig sieht es da schon etwas dünner aus. Aber wenn man sich einmal bissl tiefer reingewühlt hat, wird auch da manches klarer 😀

     
  • Andreas Höhmann 8:35 am Thursday, 4. September 2008 Permalink |
    Tags: Component, Custom, JDeveloper,   

    JDeveloper Declarative JSF Components 

    Auf dieser Seite findet sich ein Screencast in dem Shay Shmeltzer vom Oracle JDeveloper Team die Anwendung von Declarativen JSF Komponenten zeigt. Er hat zwei Projekte, in dem einen definiere er CustomerComponenten (Ähnlichkeiten zu Faclets) in dem anderen benutzt er diese.

     
  • Andreas Höhmann 15:08 am Tuesday, 19. August 2008 Permalink |
    Tags: , Masked Input,   

    Masked Input with Richfaces 

    I would like to define a input-mask for a inputfield, e.g. for a telefonnumber or number in scientific notation (1.23 E-10). A nice way could be the usage of a special „css-class“ to mark the masked-input-elements and the usage of the alt-attribute to define the mask-format. Here is my solution:

    First of all we need a little bit Javascript, i use the Masked Input Component from Jonas Raoni Soares Silva:

    
    addEvent = function(o, e, f, s){
    var r = o[r = "_" + (e = "on" + e)] = o[r] || (o[e] ? [[o[e], o]] : []), a, c, d;
    r[r.length] = [f, s || o], o[e] = function(e){
    try{
    (e = e || (window.event ? (arguments = [].slice.call(arguments)).unshift(e = event) || 1 && e : {})).preventDefault || (e.preventDefault = function(){e.returnValue = false;});
    e.stopPropagation || (e.stopPropagation = function(){e.cancelBubble = true;});
    e.target || (e.target = e.srcElement || null);
    e.key = (e.which + 1 || e.keyCode + 1) - 1 || 0;
    }catch(f){}
    for(d = 1, f = r.length; f; r[--f] && (a = r[f][0], o = r[f][1], a.apply ? c = a.apply(o, arguments) : (o._ = a, c = o._(e), o._ = null), d &= c !== false));
    return e = null, !!d;
    }
    };
    removeEvent = function(o, e, f, s){
    for(var i = (e = o["_on" + e] || []).length; i;)
    if(e[--i] && e[i][0] == f && (s || o) == e[i][1])
    return delete e[i];
    return false;
    };
    MaskInput = function(f, m){
    function mask(e){
    var patterns = {"1": /[A-Z]/i, "2": /[0-9]/, "4": /[\xC0-\xFF]/i, "8": /./ },
    rules = { "a": 3, "A": 7, "9": 2, "C":5, "c": 1, "*": 8};
    function accept(c, rule){
    for(var i = 1, r = rules[rule] || 0; i <= r; i<<=1)
    if(r & i && patterns[i].test(c))
    break;
    return i <= r || c == rule;
    }
    var k, mC, r, c = String.fromCharCode(k = e.key), l = f.value.length;
    (!k || k == 8 ? 1 : (r = /^(.)\^(.*)$/.exec(m)) && (r[0] = r[2].indexOf(c) + 1) + 1 ?
    r[1] == "O" ? r[0] : r[1] == "E" ? !r[0] : accept(c, r[1]) || r[0]
    : (l = (f.value += m.substr(l, (r = /[A|9|C|\*]/i.exec(m.substr(l))) ?
    r.index : l)).length) < m.length && accept(c, m.charAt(l))) || e.preventDefault();
    };
    for(var i in !/^(.)\^(.*)$/.test(m) && (f.maxLength = m.length), {keypress: 0, keyup: 1})
    addEvent(f, i, mask);
    };
    

    then we define the inputfield:

    <h:inputText styleClass="maskedInput" alt="9,99 E-99"/>
    

    and last a little bit JS-Code which decorates the current formulare after each rerender, but it at the end of your page:

    <a4j:outputPanel ajaxRendered="true">
      <script type="text/javascript">
       var maskedInput=$$(".maskedInput");
       if (Object.isArray(maskedInput)){
         maskedInput.each(function (elm){
         // input-mask is defined in the alt element
         var maskPattern = Element.readAttribute(elm, "alt");
         MaskInput(elm, maskPattern);
         });
       }
      </script>
    </a4j:outputPanel>
    

    That’s it 🙂 Every time A4J rerenders the page the decorator search all marked elements and creates a MaskInput object for the elements.

    Try it!

     
  • Andreas Höhmann 17:00 am Friday, 25. April 2008 Permalink |
    Tags: Converter, Enum,   

    EnumConverter 

    Today i found a very simple solution for using a Enumeration with a converter in JSF. Other than Rick Hightowers or Dave Brondsena i used the hidden javax.faces.convert.EnumConverter. Why hidden? I can’t find no reference in the JSF 1.2 documentation. But both implementations MyFaces and SunRI contains such a class. And the class is not depreacated. So … let’s use it 🙂

    public enum EnumType {
       A1, A2, A3, A4,
    }
    public class EnumTypeConverter extends EnumConverter {
      public EnumTypeConverter() {
        super(EnumType.class);
      }
    }
    
      <converter>
        <converter-id>ENUM</converter-id>
        <converter-class>de.ahoehma.jsf.converter.EnumTypeConverter</converter-class>
      </converter>
    
    <h:selectOneMenu id="type" value="#{bean.type}">
      <f:converter converterId="ENUM"/>
      <f:selectItem itemLabel="Type A1" itemValue="A1"/>
      <f:selectItem itemLabel="Type A2" itemValue="A2"/>
      <f:selectItem itemLabel="Type A3" itemValue="A3"/>
      <f:selectItem itemLabel="Type A4" itemValue="A4"/>
    </h:selectOneMenu>
    
     
  • Andreas Höhmann 14:43 am Monday, 31. March 2008 Permalink |
    Tags: , , File Download, , MyFaces,   

    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, , 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, , ,   

    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