Q:

p:dataTable and p:commandLink not working

I have the following:
<p:dataTable id="photoPreview" var="file" value="#{ fileUploadMB.filesList }">
  <p:column>
    <p:commandLink ajax="false">
      <p:graphicImage value="#{ fileUploadMB.streamedContentFromFile }" rendered="#{ file.fileIsImage }">
        <f:param name="N" value="#{ file.N() }" />
        <f:param name="size" value="thumbnail" /> 
      </p:graphicImage>
      <p:fileDownload value="#{ fileUploadMB.streamedContentFromFile }"/>
      <f:param name="N" value="#{ file.N() }" />
    </p:commandLink>
  </p:column>
  <p:column>
     <p:commandLink value="Remove" disabled="#{ loginMB.customerRole and !orderDetailMB.fileModificationPermitted }" action="#{ fileUploadMB.removeUploadedFile }" rendered="#{ file.uploadedFilePresent }" update="photoPreview">
       <f:param name="N" value="#{ file.N() }" />
     </p:commandLink>
  </p:column>
 </p:dataTable>

But when I click on the Remove link, the dataTable is not updated.  I know that fileUploadMB.removeUploadedFile() is being called.  I know that file.isFileIsImage() is being called after it.  But nothing changes in the browser.  Firebug's Net tab shows me that the response contains nothing useful, but that not might be reliable; Firebug has a tendency to refetch URLs when you look at response sub-tab.
Relevant code for FileUploadMB :
@ManagedBean
@SessionScoped
public class FileUploadMB implements Serializable {

    private static final Logger logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());

    private Map<Integer, OrderCustomerFile> uploadedFilesList;
    private List<OrderCustomerFile> uploadedFilesToDelete;
    private static final int MAX_NUMBER_OF_FILES = 5;

    private List<FileN> filesList;

    // ----------------------------------------------------
    public boolean fileIsImage(int rowIndex) {
        if( rowIndex == 0 ) {
            logger.debug( "rowIndex={}", rowIndex );
            logger.debug( "uploadedFileList={}", uploadedFilesList );
            logger.debug( "uploadedFileList.keys={}", uploadedFilesList.keySet() );
        }
        if( !uploadedFilesList.containsKey(rowIndex) ) {
            if( rowIndex == 0 )
                logger.debug( "nope" );
            return false;
        }
        String ct = uploadedFilesList.get(rowIndex).getContentType();
        if (ct.contains("gif") || ct.contains("jpeg") || ct.contains("png") || ct.contains("jpe"))
            return true;
        else
            return false;
    }
    // ----------------------------------------------------
    public void removeUploadedFile() {
        FacesContext context = FacesContext.getCurrentInstance();
        logger.debug( "context={}", context );
        String N = context.getExternalContext().getRequestParameterMap().get("N");
        logger.debug( "N={}", N );
        removeUploadedFile( Integer.valueOf( N ) );
    }

    // ----------------------------------------------------
    public void removeUploadedFile(int rowIndex) {
        logger.debug( "uploadedFileList={}", uploadedFilesList );
        logger.debug( "uploadedFileList.keys={}", uploadedFilesList.keySet() );
        logger.debug( "rowIndex={}", rowIndex );
        if( uploadedFilesList.get(rowIndex).getUploadedFile() == null ) {
            logger.debug( "Wants to delete {}", uploadedFilesList.get(rowIndex).getFilePath() );
            uploadedFilesToDelete.add( uploadedFilesList.get(rowIndex) );
        }
        uploadedFilesList.remove( rowIndex );
        logger.debug( "uploadedFileList={}", uploadedFilesList );
        logger.debug( "uploadedFileList.keys={}", uploadedFilesList.keySet() );
    }

    // ----------------------------------------------------
    public List<FileN> getFilesList() {
        if( filesList == null )
            buildFilesList();  
        //logger.debug( "filesList={}", filesList );
        return filesList;
    }    

    public void buildFilesList() {
        Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
        filesList = new ArrayList<FileN> ();
        for( int q=0; q<MAX_NUMBER_OF_FILES; q++ ) {
            FileN file = new FileN (this, q);
            filesList.add( file );
            logger.debug( "file={} hashCode={}", file, file.hashCode() );
            viewMap.put( String.valueOf( file.hashCode() ), file );
        }
    }

    // ----------------------------------------------------
    public StreamedContent getStreamedContentFromFile() {  
        // See http://stackoverflow.com/questions/8207325/display-image-from-database-with-pgraphicimage/12452144#12452144
        FacesContext context = FacesContext.getCurrentInstance();
        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            logger.debug( "RENDER_RESPONSE phase" );
            // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
            return new DefaultStreamedContent();
        }
        String N = context.getExternalContext().getRequestParameterMap().get("N");
        String size = context.getExternalContext().getRequestParameterMap().get("size");
        logger.debug( "N={}, size={}", N, size );        
        return streamedContentFromFile( Integer.valueOf( N ), size );
    }
    // ----------------------------------------------------
    public StreamedContent streamedContentFromFile( int rowIndex ) {
        return streamedContentFromFile( rowIndex, "normal" );
    }
    // ----------------------------------------------------
    public StreamedContent streamedContentFromFile( int rowIndex, String size ) {
        logger.debug( "rowIndex={}", rowIndex );

        //fichier provenant de la BD (ayant deja ete sauvegarde)
        if (uploadedFilesList.get(rowIndex).getFilePath() != null) {
            OrderCustomerFile iof = uploadedFilesList.get( rowIndex );
            try {
                java.io.File file = new java.io.File( iof.getFilePath() );
                if (file.exists()) {
                    logger.debug( "Streaming file {} ct={}", iof.getFilePath(), iof.getContentType() );
                    BufferedInputStream bif = new BufferedInputStream(
                                                new FileInputStream(  
                                                    new java.io.File(iof.getFilePath())));
                    logger.debug( "bif={}", bif );
                    String name = iof.getFilenameOriginal();
                    if( name == null )
                        name = iof.getFilePath().substring(iof.getFilePath().lastIndexOf("/")+1);
                    logger.debug( "name={}", name );
                    return new DefaultStreamedContent(bif, iof.getContentType(), name );
                }
            }    
            catch (FileNotFoundException e ) {
                logger.error( null, e );
                MessagesMB.showErrorMessage(MessagesMB.ORDER_DETAILS_FILE_NOT_FOUND);
            }

        //fichier n'ayant pas encore ete sauvegarde dans la BD    
        } 
        else if (null != uploadedFilesList.get(rowIndex).getUploadedFile()) {
            try {
                OrderCustomerFile file = uploadedFilesList.get(rowIndex);
                return new DefaultStreamedContent( file.getUploadedFile().getInputstream(),
                                                   file.getContentType(),
                                                   file.getFilenameOriginal()
                                                 );
            } 
            catch (IOException e ) {
                logger.error( null, e );
                MessagesMB.showErrorMessage(MessagesMB.ERROR_GENERAL);
            }
        }

        return null;
    }

    // ----------------------------------------------------
    public boolean uploadedFilePresent(int rowIndex) {
        return uploadedFilesList.containsKey(rowIndex); // && !hasBeenPersisted( rowIndex );
    }

}

Relevant parts of FileN:
@ManagedBean
@ViewScoped
public class FileN implements Serializable {

    private static final Logger logger =
            LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());

    private Integer N;
    private FileUploadMB fileUploadMB;

    // ----------------------------------------------------
    /** Creates a new instance of File */
    public FileN( FileUploadMB fileUploadMB, Integer N ) {
        this.N = N;
        this.fileUploadMB = fileUploadMB;
        logger.debug( "N={}", N );
    }
    // ----------------------------------------------------
    public int N () {
        return N.intValue();
    }
    // ----------------------------------------------------
    public String getN () {
        return N.toString();
    }

    // ----------------------------------------------------
    public boolean isFileIsImage () {
        if( N.equals( 0 ) ) logger.debug( "N={}", N );
        return fileUploadMB.fileIsImage( N.intValue() );
    }

    // ----------------------------------------------------
    public boolean isUploadedFilePresent () {
        return fileUploadMB.uploadedFilePresent( N );
    }

}

(And yes, I probably have a lot of other problems, what with FileN not really being a View-scoped Bean.)
Using JSF-2.1, Primefaces-3.0, Glassfish-3.1.2.2, jdk-1.7.0
Firebug Net response is:
<?xml version='1.0' encoding='UTF-8'?>
<partial-response><changes><update id="j_idt86"><![CDATA[<div id="j_idt86" class="ui-messages ui-widget"></div>]]></update><update id="javax.faces.ViewState"><![CDATA[3615293429385705722:7636138965730106845]]></update></changes></partial-response>

HttpFox gives me the same thing:
<partial-response>
<changes>
<update id="j_idt86">
<div id="j_idt86" class="ui-messages ui-widget"></div>
</update>
<update id="javax.faces.ViewState">-2752869461800547313:-2875920138022122822</update>
</changes>
</partial-response>

This is over https, so getting an tcpdump would be harder.

A:

Using Primefaces-3.0

The <p:dataTable> has in older PF 3.x versions a bug which caused that the <p:dataTable> can't be ajax-updated by a command which originated inside the <p:dataTable> itself.
You have 2 options:

Upgrade to at least PrimeFaces 3.4.
Ajax-update a parent component instead. Perhaps @form?

The alternative option would be to turn off ajax by ajax="false".

