Monday, March 25, 2013

JSF 2.1 Tip of the Day: RichFaces 4.3.x Drag and Drop

Multiple DnD Targets
The Primefaces version of my drag-n-drop (DnD) for JSF has been very popular. I didn't want my friends at Red Hat feeling left out. I did a couple of examples using drag-n-drop with the same arrow demo as the Primefaces example: JSF 2.1.x Tip of the Day: Primefaces 3.5 <p:ring/> Drag-N-Drop Example.

I did have a little more fun with using Java to clone some of the arrows so that you could see how to use them multiple times.

Requirements

Details

The project was developed using NetBeans 7.3 and Apache Maven. The Mercurial project can be found on BitBucket here: richfaces-dnd-examples

Code


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:rich="http://richfaces.org/rich">
    <h:head>
        <title>Multiple Target Panels</title>
    </h:head>
    <h:body>
        <h1>
Multiple Target Panels</h1>
<p>
This example has both vertical and horizontal arrow stacks. The Horizontal target removes the arrow from the source.
  
            The Vertical target clones the arrow and allows multiple arrows of the same name.
        </p>
<h:form id="form1">
            <rich:dragIndicator id="dragIndicator1"/>
            <h:panelGrid columns="2">
 
                <rich:panel id="target">
                    <f:facet name="header">
                        <h:outputText value="Target Panel"/>
                    </f:facet>
 
                    <h:panelGrid id="panelGrid1" columns="2">
                        <h:outputText value="Horizontal Arrow Column" rendered="#{arrowBean.target.size() eq 0}"/>
                        <rich:dataGrid id="hArrowPanel"
                                       columns="#{arrowBean.target.size() lt 10 ? arrowBean.target.size() : 10 }"
                                       elements="10" value="#{arrowBean.target}" var="hArrow">
                            <a4j:outputPanel id="outputPanel4">
                                <h:graphicImage id="graphicImage4" alt="#{hArrow.name}" value="#{resource['images:arrow-md.png']}"/>
                                <h:outputText id="outputText4" value="#{hArrow.name}"/>
                            </a4j:outputPanel>
                        </rich:dataGrid>
                        <h:graphicImage id="graphicImage2" alt="bullseye" height="100" width="100"  value="#{resource['images:bullseye.jpg']}">
                            <rich:dropTarget  id="dropTarget1"
                                              acceptedTypes="a"
                                              dropValue="hArrow"
                                              dropListener="#{arrowListener.processDrop}"
                                              render="source,target"/>
                        </h:graphicImage>
                    </h:panelGrid>
 
                    <h:panelGrid id="panelGrid2" columns="1">
                        <h:outputText value="Vertical Arrow Column (Clone)" rendered="#{arrowBean.serialTarget.size() eq 0}"/>
                        <rich:dataGrid id="vArrowPanel" columns="1" value="#{arrowBean.serialTarget}" var="vArrow"
                                       elements="10" rendered="#{arrowBean.serialTarget.size() gt 0}">
                            <a4j:outputPanel id="outputPanel3">
                                <h:graphicImage id="graphicImage3" alt="#{vArrow.name}" value="#{resource['images:arrow-md.png']}"/>
                                <h:outputText id="outputText3" value="#{vArrow.name}"/>
                            </a4j:outputPanel>
                        </rich:dataGrid>
                        <h:graphicImage id="graphicImage5" alt="bullseye" height="100" width="100"  value="#{resource['images:bullseye.jpg']}">
                            <rich:dropTarget  id="dropTarget2"
                                              acceptedTypes="a"
                                              dropValue="s"
                                              dropListener="#{arrowListener.processDrop}"
                                              render="source,target"/>
                        </h:graphicImage>
                    </h:panelGrid>
                </rich:panel>
 
                <rich:panel id="source">
                    <f:facet name="header">
                        <h:outputText value="Arrows"/>
                    </f:facet>
                    <rich:dataTable id="sourceDataTable" value="#{arrowBean.source}" var="arrow">
                        <rich:column id="column1">
                            <a4j:outputPanel id="sourcePanel">
                                <rich:dragSource id="dragSource1" type="a" dragValue="#{arrow}"/>
                                <h:graphicImage id="graphicImage1" alt="#{arrow.name}" value="#{resource['images:arrow-md.png']}"/>
                                <h:outputText id="outputText1" value="#{arrow.name}"/>
                            </a4j:outputPanel>
                        </rich:column>
                    </rich:dataTable>
                </rich:panel>
            </h:panelGrid>
 
            <h:panelGroup>
                <h:button outcome="index" value="Home"/>
                <a4j:commandButton action="#{arrowBean.reset()}" value="Reset" render="target source"/>
            </h:panelGroup>
        </h:form>
    </h:body>
</html>

Patterns for Scaling Mercurial and Git

I just finished reading over the Refcard Git Patterns and Anti-PatternsScaling from Workgroup to Enterprise by Luca Milanesio from GerritForge. He has done a great job describing patterns and anti-patterns that don't just apply to Git, but any distributed source control system. The obvious focus of this Refcard is Git, but the patterns are essentially the same sans the commands.

I thought that the card was well done, and is a nice and easy read.

Friday, March 22, 2013

JSF 2.1.x Tip of the Day: Primefaces 3.5 <p:ring/> Drag-N-Drop Example

Camp Old Indian
I was trying to come up with an example to demonstrate <p:ring /> along with Drag-and-Drop (DnD). I had an idea of using a target to drop things on. I was sitting in my living room watching my youngest son creating a world on little big planet, and I remembered his first time shooting a bow. He had a perfect bullseye.

I decided that I would drag-n-drop arrows in honor of my son. I mentioned the idea to him, and he loved it. So here it is... a useful example that was fun to create. In this example, I will demonstrate the <p:ring />, and <p:fieldSet /> components primarily. I have added some effects to a <p:dialog /> if you select an arrow and dismiss it.






Requirements

I used NetBeans 7.3 to develop the Apache Maven based project. The Mercurial project can be found on BitBucket here: primefaces-ring-dnd.

Example

index.xhtml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Arrow Panel</title>
    </h:head>
    <h:body>
        <h:form id="form1">
 
            <p:ring id="arrowRing" var="arrow" value="#{indexBean.arrows}">
                <p:commandLink id="arrowLink" update=":form1:arrowDetails" oncomplete="arrowDialog.show()">
                    <p:graphicImage value="#{resource['images:arrow-md.png']}"/>
                    <h:outputText value="#{arrow.name}"/>
                    <f:setPropertyActionListener value="#{arrow}" target="#{indexBean.selectedArrow}" />
                </p:commandLink>
                <p:draggable for="arrowLink" helper="clone" />
            </p:ring>
 
            <p:outputPanel id="bullseyePanel" style="width: 100%; margin-left: auto;margin-right: auto;">
                <p:graphicImage id="bullseye" name="bullseye.jpg" library="images" style="margin-left: auto;margin-right: auto;"/>
                <p:fieldset id="arrowFieldSet" toggleable="true" legend="Fired Arrows" collapsed="true" >
                    <p:panel id="firedArrows">
                        <p:dataGrid id="firedArrowGrid" value="#{indexBean.firedArrows}" var="arrow" columns="1"
                                    rendered="#{not empty indexBean.firedArrows}" >
                            <p:column>
                                <p:commandLink id="dp" update=":form1:arrowDetails" oncomplete="arrowDialog.show()" title="View Detail">
                                    <p:graphicImage value="#{resource['images:arrow-md.png']}"/>
                                    <f:setPropertyActionListener value="#{arrow}" target="#{indexBean.selectedArrow}" />
                                </p:commandLink>
                            </p:column>
                            <p:column headerText="Name">
                                <h:outputText value="#{arrow.name} - "/>
                            </p:column>
                            <p:column headerText="Description">
                                <h:outputText value="#{arrow.description}"/>
                            </p:column>
                        </p:dataGrid>
                    </p:panel>
                </p:fieldset>
            </p:outputPanel>
 
            <p:droppable for="bullseye" tolerance="touch" datasource="arrowRing">
                <p:ajax listener="#{indexBean.onArrowDrop}" update="arrowRing firedArrows"/>
            </p:droppable>
 
            <p:dialog header="Arrow Details" widgetVar="arrowDialog" width="800" height="250"
                      modal="true" showEffect="fade" hideEffect="explode">
                <p:outputPanel id="arrowDetails" style="text-align:center;" layout="block">
                    <p:graphicImage value="#{resource['images:arrow-md.png']}"/>
                    <h:panelGrid  columns="2" cellpadding="5">
                        <h:outputLabel for="id" value="ID: " />
                        <h:outputText id="id" value="#{indexBean.selectedArrow.id}" />
                        <h:outputLabel for="name" value="Name: " />
                        <h:outputText id="name" value="#{indexBean.selectedArrow.name}" />
                        <h:outputLabel for="description" value="Description: " />
                        <h:outputText id="description" value="#{indexBean.selectedArrow.description}" />
                    </h:panelGrid>
                </p:outputPanel>
            </p:dialog>
        </h:form>
    </h:body>
</html>

IndexBean.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.bluelotussoftware.example.jsf;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.primefaces.event.DragDropEvent;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
@ManagedBean
@ViewScoped
public class IndexBean implements Serializable {
 
    private List<arrow> arrows;
    private List<arrow> firedArrows;
    private Arrow selectedArrow;
 
    public IndexBean() {
        arrows = new ArrayList<arrow>(10);
        firedArrows = new ArrayList<arrow>(10);
 
        for (long n = 0; n < 10L; n++) {
            arrows.add(new Arrow(n, "Arrow " + n, "I am an Arrow. There are many arrows like me, but I am number #" + n));
        }
    }
 
    public Arrow getSelectedArrow() {
        return selectedArrow;
    }
 
    public void setSelectedArrow(Arrow selectedArrow) {
        this.selectedArrow = selectedArrow;
    }
 
    public List<Arrow> getArrows() {
        return arrows;
    }
 
    public void setFiredArrows(List<arrow> firedArrows) {
        this.firedArrows = firedArrows;
    }
 
    public List<arrow> getFiredArrows() {
        return firedArrows;
    }
 
    public void onArrowDrop(DragDropEvent ddEvent) {
        Arrow arrow = ((Arrow) ddEvent.getData());
        firedArrows.add(arrow);
        arrows.remove(arrow);
    }
}

Arrow.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.bluelotussoftware.example.jsf;
 
import java.io.Serializable;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
public class Arrow implements Cloneable, Serializable {
 
    private long id;
    private String name;
    private String description;
 
    public Arrow() {
    }
 
    public Arrow(long id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    protected Arrow clone() throws CloneNotSupportedException {
        return (Arrow) super.clone();
    }
 
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Arrow other = (Arrow) obj;
        if (this.id != other.id) {
            return false;
        }
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }
        return true;
    }
 
    @Override
    public int hashCode() {
        int hash = 5;
        hash = 83 * hash + (int) (this.id ^ (this.id >>> 32));
        hash = 83 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 83 * hash + (this.description != null ? this.description.hashCode() : 0);
        return hash;
    }
}

Conclusion

This is a very simple example of what you can do with a Primefaces, Mojarra, and Netbeans.
Arrow Ring and Target

Installing Mercurial 2.5.2 on IIS 7.5 and Python 2.7 64-bit

I have been given a great opportunity to move our current source control system from ClearCase to Mercurial. This includes using Active Directory for authentication and authorization. There are a number of great blog posts which helped me along the way. Please see the References at the bottom of the blog. However, I found that none of them were complete, or used older versions of the software.

Prerequisites

  • Microsoft Windows 2008 Server R2, or Microsoft Windows 7
    Note: If you are using Active Directory, the server must be a part of the domain.
  • Internet Information Server (IIS) 7.5
  • Python 2.7.3 (X86-64)
  • Mercurial 2.5.2 (X86-64)

Installation Process

Install Python

Download and install Python 2.7.3 X86-64 using the installer.

Install Mercurial

Download and install Mercurial 2.5.2 (X86-64) for use with Python 2.7.

Note: Do not install TortoiseHg. It is not required, and these directions will not work.

Install Internet Information Server (IIS)

Using the Server Manager on Windows 2008 Server R2, you need to add a role to install IIS. It is not as intuitive as it should be. On other versions of Windows, you would add it as a service.
  1. Add a Server Role.
  2. Select IIS Role.
  3. Select Role Services.
    • Application Development
      • CGI
      • ISAPI Extensions
      • ISAPI Filters
    • Security
      • Basic Authentication
      • Windows Authentication
      • Request Filtering
  4. Installation Complete

Install and Configure CGI (hgweb.cgi)

  1. Clone the mercurial repository, and copy the hgweb.cgi application to located in the project root to C:\Python27\Lib\site-packages\mercurial\hgweb.
    hg clone http://selenic.com/repo/hg
    
  2. Create a hgweb.config in C:\Python27\Lib\site-packages\mercurial\hgweb. Here is an example file:
    [web]
    encoding = UTF-8
    allow_push = *
    push_ssl = False
    contact = John Yeary
    allowzip = True
    
    [paths]
    test = C:\repos\test
    
  3. Modify the hgweb.cgi file to use the new configuration.
    #!/usr/bin/env python
    #
    # An example hgweb CGI script, edit as necessary
    # See also https://www.mercurial-scm.org/wiki/PublishingRepositories
    
    # Path to repo or hgweb config to serve (see 'hg help hgweb')
    config = "hgweb.config"
    
    
    # Uncomment and adjust if Mercurial is not installed system-wide
    # (consult "installed modules" path from 'hg debuginstall'):
    #import sys; sys.path.insert(0, "/path/to/python/lib")
    
    # Uncomment to send python tracebacks to the browser if an error occurs:
    #import cgitb; cgitb.enable()
    
    from mercurial import demandimport; demandimport.enable()
    from mercurial.hgweb import hgweb, wsgicgi
    application = hgweb(config)
    wsgicgi.launch(application)
    

Setup IIS Python CGI Handler

  1. Add a Virtual Directory to the Default Web Site
    • Alias: hg
    • Physical Path: C:\Python27\Lib\site-packages\mercurial\hgweb
  2. Select Handler Mapping from the newly created hg virtual directory.
  3. Click Actions --> Add Script Map
  4. Set the following parameters:
    • Request Path: *.cgi
    • Executable: C:\Python27\python.exe -u "%s"
    • Name: Python
  5. Restart IIS Server

Create Mercurial Test Repository

c:\>mkdir repos

c:\>cd repos

c:\repos>mkdir test

c:\repos>cd test

c:\repos\test>hg init

c:\repos\test>echo "THIS IS A TEST FILE" > README

c:\repos\test>hg add README

c:\repos\test>hg commit -u jyeary -m "Initial Import"

c:\repos\test>hg summary
parent: 0:d5c9f65c740a tip
 Initial Import
branch: default
commit: (clean)
update: (current)

Test Configuration

Using a browser, go to http://localhost/hg/hgweb.cgi. You should see the following:

Here is the changeset for the initial import

URL Rewrite (OPTIONAL)

If you want a cleaner URL for users, you will want to "prettify" your URLs. Luckily, Microsoft has a URL Rewrite plugin for IIS. You will want to install it from the link provided.
Here is the configuration for our application:
  1. Select URL Rewrite from the hg virtual directory.
  2. Create a new blank rule.
  3. Create a Rule with the following parameters:
    • Name: hgweb.cgi
    • Match URL:
      • Using: Wildcards
      • Pattern: *
    • Conditions
      • Add Condition
      • Condition Input: {REQUEST_FILENAME}
      • Check if input string: Is Not A File
    • Action
      • Action Type: Rewrite
      • Rewrite URL: hgweb.cgi/{R:1}
  4. Apply Changes
  5. Modify the hgweb.config and add the baseurl attribute.
    [web]
    encoding = UTF-8
    baseurl= /hg
    allow_push = *
    push_ssl = False
    contact = John Yeary
    allowzip = True
    
    [paths]
    test = C:\repos\test
    
The URL should be much easier now http://localhost/hg/test/

Basic Authentication (Active Directory> for push (OPTIONAL)

The default setup from the hgweb.config allows push from all clients. If we want to add more security, we need to specify authentication, and who is allowed to push.

Note: Basic Authentication passes username and password in clear text. You should use Basic Authentication in conjunction with SSL.

In the default configuration provided, you can push back any changes as shown below.
C:\Users\jyeary\Desktop>hg clone http://localhost/hg/test
destination directory: test
requesting all changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

C:\Users\jyeary\Desktop>cd test

C:\Users\jyeary\Desktop\test>ls
README

C:\Users\jyeary\Desktop\test>touch FILE1

C:\Users\jyeary\Desktop\test>touch FILE2

C:\Users\jyeary\Desktop\test>hg add FILE1

C:\Users\jyeary\Desktop\test>hg add FILE2

C:\Users\jyeary\Desktop\test>hg commit -u jyeary -m "Added files."

C:\Users\jyeary\Desktop\test>hg push
pushing to http://localhost/hg/test
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 2 changes to 2 files
We will configure our application and the server to use authentication.
  1. Select the hg virtual directory.
  2. Select Authentication
  3. Select Basic Authentication and Enable it.
  4. Edit the settings for your Active Directory (AD) domain.
    • Domain: THIS IS YOUR AD DOMAIN
    • Realm: You can call this anything that you like, I personally use the domain name.
  5. You have set up the authentication, but it still will allow you to push files as demonstrated below.
    C:\Users\jyeary\Desktop\test>touch FILE3
    
    C:\Users\jyeary\Desktop\test>hg add FILE3
    
    C:\Users\jyeary\Desktop\test>hg commit -u jyeary -m "Added FILE3"
    
    C:\Users\jyeary\Desktop\test>hg push
    pushing to http://localhost/hg/test
    searching for changes
    remote: adding changesets
    remote: adding manifests
    remote: adding file changes
    remote: added 1 changesets with 1 changes to 1 files
    
  6. To enable push to require authentication, we need to modify the hgweb.config file. We will modify allow_push = *, and set it to allow push from only certain individuals.
    [web]
    encoding = UTF-8
    baseurl= /hg
    allow_push = jyeary
    push_ssl = False
    contact = John Yeary
    allowzip = True
    
    [paths]
    test = C:\repos\test
    

    Since we have set push to accept only jyeary, it will ask us to authenticate and shown below.
    C:\Users\jyeary\Desktop\test>touch FILE4
    
    C:\Users\jyeary\Desktop\test>hg add FILE4
    
    C:\Users\jyeary\Desktop\test>hg commit -u jyeary -m "Added FILE4"
    
    C:\Users\jyeary\Desktop\test>hg push
    pushing to http://localhost/hg/test
    searching for changes
    http authorization required
    realm: mydomain.com
    user: jyeary
    password:
    remote: adding changesets
    remote: adding manifests
    remote: adding file changes
    remote: added 1 changesets with 1 changes to 1 files
    

Enable SSL (OPTIONAL)

I am indicating that this is optional, but if you are using Basic Authentication; it really should be required. This shows how to create a simple self-signed certificate. In a production environment, you will want to have a certificate signed by Certification Authoritity (CA).
  1. Select the server, and select Server Certificates.
  2. Create a self-signed certificate.

  3. Right-click on the Default Web Site, and select Edit Bindings.
  4. Select Add a Binding
  5. Add an SSL binding as follows:
    • Type: https
    • Port: 443
    • SSL Certificate: selfSigned
  6. Modify the hgweb.config file, and remove the push_ssl = False line.
  7. Test the new configuration by cloning the secured repository
    Note: Unless you add the certificate fingerprint to your hgrc, you will need to use the --insecure flag with mercurial.
    C:\Users\jyeary\Desktop>hg clone --insecure https://localhost/hg/test
    warning: localhost certificate with fingerprint e5:32:64:97:6e:10:43:e5:c7:71:92:9f:08:20:e9:4d:54:d6:c3:04 not verified (check hostfingerprints or web.cacerts config setting)
    
    destination directory: test
    requesting all changes
    adding changesets
    adding manifests
    adding file changes
    added 4 changesets with 5 changes to 5 files
    warning: localhost certificate with fingerprint e5:32:64:97:6e:10:43:e5:c7:71:92:9f:08:20:e9:4d:54:d6:c3:04 not verified (check hostfingerprints or web.cacerts config setting)
    
    updating to branch default
    5 files updated, 0 files merged, 0 files removed, 0 files unresolved
    

Conclusion

Setting up Mercurial is easy to do in a Windows environment. You will gain all of the great benefits from Mercurial along with active directory. It is much easier to setup Mercurial on IIS for use with Active Directory than on a LAMP stack with winbind, or LDAP. If AD is a requirement... this is definitely the solution.

References

Thursday, March 14, 2013

JSF Tip of the Day: PrimeFaces 3.0 Tree Drag and Drop Example

I worked on a proof of concept for a drag and drop enabled <p:tree /> for a project I was working on. At the time, PrimeFaces was just starting the path to 3.0, and I was working with a snapshot. The POC was part of a decision making process on which JSF framework, or combination of frameworks to use.

This drag and drop example was part of the the work Optimus Prime and his team created as a potential functionality for the <p:tree /> component. In the final release, this functionality was removed. I still have high hopes that in PrimeFaces 3.5.+ that it will be added back into the product. It was a cool idea and one that we had hoped to take advantage of in our products.

If you need this functionality currently, and don't mind using a 3.0-SNAPSHOT release, you can have a drag and drop <p:tree />. I published this since it was a good example, and I thought someone might need this specific functionality. Otherwise it is code lost in time.

The Mercurial project can be pulled from BitBucket and was developed using NetBeans and Apache Maven.

The code can be found here: primefaces-tree-dragdrop-demo

Example


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>PrimeFaces Tree</title>
    </h:head>
    <h:body>
        <h:form id="form1">
            <p:growl id="messages" showDetail="true"/>
            <p:tree value="#{treeBean.model}"
                    var="node"
                    selectionMode="single"
                    selection="#{treeBean.selectedNode}"
                    update="form1:messages"
                    nodeSelectListener="#{treeBean.onNodeSelect}"
                    dragdrop="true">
                <p:treeNode type="node">
                    <h:outputText value="#{node}"/>
                </p:treeNode>
                <p:treeNode type="leaf">
                    <h:outputText value="#{node}" />
                </p:treeNode>
            </p:tree>
        </h:form>
    </h:body>
</html>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * Copyright 2010-2013 Blue Lotus Software, LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bluelotussoftware.example.jsf;
 
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
public class TreeNodeImpl extends DefaultTreeNode {
 
    private static final long serialVersionUID = 5333810777428638968L;
 
    public TreeNodeImpl(String type, Object data, TreeNode parent) {
        super(type, data, parent);
    }
 
    public TreeNodeImpl(Object data, TreeNode parent) {
        super(data, parent);
    }
 
    @Override
    public String getType() {
        if (isLeaf()) {
            return "leaf";
        } else {
            return "node";
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/*
 * Copyright 2010-2013 Blue Lotus Software, LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bluelotussoftware.example.jsf;
 
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import org.primefaces.event.DragDropEvent;
import org.primefaces.event.NodeSelectEvent;
import org.primefaces.model.TreeNode;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
@ManagedBean
@SessionScoped
public class TreeBean implements Serializable {
 
    private static final long serialVersionUID = 2417620239014385855L;
    private TreeNode root;
    private TreeNode selectedNode;
 
    public TreeBean() {
        root = new TreeNodeImpl("Root", null);
        TreeNode node0 = new TreeNodeImpl("Segment 0", root);
        TreeNode node1 = new TreeNodeImpl("Segment 1", root);
        TreeNode node2 = new TreeNodeImpl("Segment 2", root);
        TreeNode node00 = new TreeNodeImpl("Segment 0.0", node0);
        TreeNode node01 = new TreeNodeImpl("Segment 0.1", node0);
        TreeNode node10 = new TreeNodeImpl("Segment 1.0", node1);
        TreeNode node11 = new TreeNodeImpl("Segment 1.1", node1);
        TreeNode node000 = new TreeNodeImpl("Segment 0.0.0", node00);
        TreeNode node001 = new TreeNodeImpl("Segment 0.0.1", node00);
        TreeNode node010 = new TreeNodeImpl("Segment 0.1.0", node01);
        TreeNode node100 = new TreeNodeImpl("Segment 1.0.0", node10);
    }
 
    public TreeNode getModel() {
        return root;
    }
 
    public TreeNode getSelectedNode() {
        return selectedNode;
    }
 
    public void setSelectedNode(TreeNode selectedNode) {
        this.selectedNode = selectedNode;
    }
 
    public void onNodeSelect(NodeSelectEvent event) {
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Selected", event.getTreeNode().getData().toString());
        FacesContext.getCurrentInstance().addMessage("node", msg);
    }
 
    public void onDragDrop(DragDropEvent event) {
        TreeNode node = (TreeNode) event.getData();
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "DragDrop", node + " moved to " + node.getParent());
        FacesContext.getCurrentInstance().addMessage(null, message);
    }
}

Monday, March 11, 2013

JSF 2.x Tip of the Day: What is <h:link /> and Why Do I Care?

One of the JSF Components that is new in JSF 2.1 is the <h:link /> component. This component represents an HTML <a /> (anchor) tag. In most cases, I would actually recommend using an <a /> tag over this component. The anchor tag can use Expression Language (EL) which is what most people are after. The <h:link /> is more overkill than most people need. However, there are a couple of cases where the <h:link /> really does shine.

  1. disabled="true" attribute renders a <span /> that can serve as a space holder.
  2. The rendered attribute can be programmatically controlled using EL, or via binding of the component. You don't need to use JS, or a JS Framework like jquery to hide it. It simply does not render.
  3. The component can be programatically created using a binding <h:link binding="#{bean.link}" />.
  4. Built-in support for JSF navigation using the outcome attribute.
  5. Supports passing JSF ViewParameters through the use of the includeviewParams attribute.

The real significant disadvantage is that you can't use it like a normal anchor tag and direct the href to another site without some tricks. In that case you are better off using a <h:commandLink/> or a plain <a/> tag.

References

Examples


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <f:metadata>
        <f:viewParam name="color" value="color:blue;"/>
    </f:metadata>
    <h:outputStylesheet name="default.css" library="css"/>
    <h:head>
        <title>Index</title>
    </h:head>
    <h:body>
        <a class="formatted" href="faces/other.xhtml">Other</a>
         
 
        <h:link disabled="true" value="X" styleClass="formatted"
                outcome="other"/>
         
 
        <h:link rendered="false" value="Y" outcome="other"/>
         
 
        <h:link value="Press CTRL+ALT+M" accesskey="M" outcome="#{indexBean.requestURL}"
                styleClass="formatted"/>
         
 
        <h:link value="Z" outcome="other" />
         
 
        <h:link accesskey="E" outcome="other" includeViewParams="true"/>
    </h:body>
</html>

Project Files


Here is the NetBeans 7.3 Maven Project: jsf-link-example

Java 6 File Monitoring Example

I was asked today how you would monitor a file for changes using Java 6. Since Java 7 has added some great File handling features that I use, it was a reminder that not everyone is so lucky to use Java 7. I remembered an example from Geosoft (I remembered it because I liked the name). I went looking and I found their example code FileMonitor.java. It is a simple and elegant solution to monitoring files and directories for changes.

This is by no means a complete example, but does show a simple technique.

Popular Posts