JDK-6646622 : FileSaveService hides yet enforces the extension(s)
  • Type: Bug
  • Component: deploy
  • Sub-Component: webstart
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,windows_xp
  • CPU: generic,x86
  • Submitted: 2008-01-02
  • Updated: 2011-02-16
  • Resolved: 2008-07-01
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u10 b21Fixed 7Fixed
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b06)
Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
The java.jnlp.FileSaveService (FSS) of the web start API allows even sandboxed apps. to write to the local file system.

Both of the methods* of the FSS accept a pathHint, name and extensions as possible suggestions for the end user, but warn in the JavaDocs..
"..(pathHint/name/extensions) might be ignored by the JNLP Client."

Fair enough - the ability is offered if available.

The odd and disturbing thing, is that although the extensions (and name) are indeed ignored here on this Win XP Pro box running Java 1.6.0, the user is still forced to provide a name that complies with the extensions that were never shown to them! If the application suggested .log and the user enters a file name of appreport.txt and clicks 'Save', the returned FileContents will be null.

* saveAsFileDialog/saveFileDialog

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Visit <http://www.physci.org/test/jws/fss1/#launch> and click the
'Launch Test' button (direct link is <http://www.physci.org/test/jws/fss1/fsstest.jnlp>)
2) Click the 'Save - suggested as above' button of the application that appears on-screen.
3) 'OK' the security warning re. write access
4) type anything.NotTxt in the 'File name:' field of the 'Save' dialog that appears.
5) Click the 'Save' button
6) See JOptionPane message - 'FileContents is null! ..'

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Steps 1-5 identical.
6) JOptionPane message 'anything.NotTxt file saved!'
ACTUAL -
...
6) See JOptionPane message - 'FileContents is null! ..'

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error messages as such, just the odd behaviour that the end user is expected to guess the file extensions they were never shown.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/**
Full source, JNLP template and ant build file are availiable at..
<http://www.physci.org/test/jws/fss1/fsstest.zip>
*/

package test;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.EventQueue;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JOptionPane;

import javax.swing.border.EmptyBorder;

// IO classes used by this sandboxed app.
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

// The FileSaveService of interest in this example
import javax.jnlp.FileSaveService;
import javax.jnlp.FileContents;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;

/**
This demo. might display some odd behaviour I noticed, when using
Java 1.6.0_02 on Win XP Pro.

The FileSaveService will fail to hint the extensions to the user,
yet still enforce them!  So unless the user specifies a filename
that complies with the suggested (but hidden from the user)
file extension, the FileContents returned when the user clicks
'Save' will be null!

The dialog is more forgiving if we feed it 'null' for the
file extensions - then the user can save to whatevr name they
like.

** Warning - this class will happily overwrite any file
you point it to, though if the file exists already, the
save service will prompt for if you /really/ want to do
that. **

@author Andrew Thompson
*/
public class FileTypeFSSTest
  extends JFrame {

  FileSaveService fileSaveService;
  JTextField path;
  JTextField extension;
  JTextField name;

  FileTypeFSSTest() {
    super( "File Type FileSaveService Test" );
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel mainUI = new JPanel(new BorderLayout(5,5));

    JPanel labels = new JPanel(new GridLayout(0,1,2,2));
    JPanel textFields = new JPanel(new GridLayout(0,1,2,2));

    labels.add( new JLabel("Path") );
    path = new JTextField(".",20);
    textFields.add(path);

    labels.add( new JLabel("Name") );
    name = new JTextField("eg",20);
    textFields.add(name);

    labels.add( new JLabel("Extension") );
    extension = new JTextField("txt",20);
    textFields.add(extension);

    JPanel buttonPanel = new JPanel( new GridLayout(0,1,3,3) );
    JButton saveAs = new JButton("Save - suggest as above");
    saveAs.addActionListener( new ActionListener(){
        public void actionPerformed(ActionEvent ae) {
          byte[] content = FileTypeFSSTest.this.
            toString().getBytes();

          String pathText = path.getText();
          if ( pathText.trim().length()==0 ) {
            pathText = null;
          }

          String nameText = name.getText();
          if ( nameText.trim().length()==0 ) {
            nameText = null;
          }

          String extensionText = extension.getText();
          if ( extensionText.trim().length()==0 ) {
            extensionText = null;
          }

          saveFile(
            pathText,
            nameText,
            extensionText,
            content);
        }
      } );
    buttonPanel.add( saveAs );

    JButton saveNull = new JButton("Save - no suggestions");
    saveNull.addActionListener( new ActionListener(){
        public void actionPerformed(ActionEvent ae) {
          byte[] content = toString().getBytes();

          saveFile(null,null,null,content);
        }
      } );
    buttonPanel.add( saveNull );

    mainUI.add( labels, BorderLayout.WEST );
    mainUI.add( textFields, BorderLayout.CENTER );
    mainUI.add( buttonPanel, BorderLayout.SOUTH );

    mainUI.setBorder(new EmptyBorder(3,3,3,3));
    setContentPane(mainUI);

    try {
      fileSaveService = (FileSaveService)ServiceManager.lookup(
        "javax.jnlp.FileSaveService"
        );
    } catch(UnavailableServiceException e) {
      e.printStackTrace();
    }

    pack();
    setLocationRelativeTo(null);

    System.out.println( getPropertiesString() );
  }

  /** path, name, and (singular) extension are all suggested for
  the file to be written from this content. */
  public void saveFile(
    String path,
    String name,
    String extension,
    byte[] content) {

    ByteArrayInputStream bais = new ByteArrayInputStream(
      content );
    String[] xtns = {
      extension
    };
    if (extension==null) {
      xtns = null;
    }
    try {
      FileContents fc = fileSaveService.
        saveFileDialog( path, xtns, bais, name );
      String message;
      if( fc==null ) {
        message = "FileContents is null!  Suggested: " +
          path +
          "/" +
          name +
          "." +
          extension;
      } else {
        message = fc.getName() + " file saved!";
      }
      System.out.println(message);
      JOptionPane.showMessageDialog( this, message );
    } catch(IOException ioe) {
      ioe.printStackTrace();
    }
  }

  public String toString() {
    return "FileTypeFSSTest: path: '" + path.getText() +
      "' name: '" + name.getText() +
      "' extension: '" + extension.getText() + "'";
  }

  public String getPropertiesString() {
    StringBuffer sb = new StringBuffer();
    String eol = System.getProperty("line.separator");

    String prop = "java.vm.version";
    sb.append( prop );
    sb.append( ": " );
    sb.append( System.getProperty(prop) );
    sb.append( eol );

    prop = "java.vendor";
    sb.append( prop );
    sb.append( ": " );
    sb.append( System.getProperty(prop) );
    sb.append( eol );

    prop = "os.arch";
    sb.append( prop );
    sb.append( ": " );
    sb.append( System.getProperty(prop) );
    sb.append( eol );

    prop = "os.version";
    sb.append( prop );
    sb.append( ": " );
    sb.append( System.getProperty(prop) );
    sb.append( eol );

    prop = "os.name";
    sb.append( prop );
    sb.append( ": " );
    sb.append( System.getProperty(prop) );
    sb.append( eol );

    return sb.toString();
  }

  /** Run the class. */
  public static void main(String[] args) {
    Runnable r = new Runnable() {
      public void run() {
        FileTypeFSSTest frame = new FileTypeFSSTest();
        frame.setVisible(true);
      }
    };
    EventQueue.invokeLater(r);
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
a) do not suggest extensions, pass null
b) forewarn the end user (perhaps via a JOptionPane) of the expected extension(s) immediately before the call to either of the FileSaveService methods.

Neither of these is very good as a 'workaround', but they are the best (kludgy) ideas I have come up with.

Comments
EVALUATION caused by implementation of 5025269
20-02-2008