Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 13. (The basics) Progress Bars, Sliders, and Scroll Bars. Easy for reading, Click here!

Custom Search
Swing Chapter 13. (The basics) Progress Bars, Sliders, and Scroll Bars. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 5/5 



Previous Page Previous Page (4/5)
Subpages: 1. JBounded-range components overview 
2.
Basic JScrollBar example 
3.
JSlider date chooser 
4. JSliders in a JPEG image editor 
5. JProgressBar in an FTP client application 

13.5  JProgressBar in an FTP client application

The following example uses JProgressBar to display progress in downloading and uploading files using File Transfer Protocol (FTP). The support for this protocol is provided in the sun.net and sun.net.ftp packages.

13.5.1  FtpClient

class sun.net.ftp.FtpClient

This class provides functionality for an FTP client. Methods particularly relevant to this example include:

FTPClient(String host): constructor to create a new instance and connect to the given host address.

login(String user, String password): login to an FTP host with given username and password.

cd(String directory): change directory.

binary(): set mode to binary for proper file transfering.

closeSever(): disconnect from host.

list(): returns an InputStream supplying the printout of the ls -l command (list contents of directories, oner per line).

get(String filename): returns an InputStream for retrieving the specified file from the host.

put(String filename): returns an OutputStream for writing the specified file to the host.

Note: This application's GUI is layed out using our custom DialogLayout2 layout manager, developed in chapter 4. Refer back to this chapter for more information about how this manager works.

Figure 13.8 FTP Client application with JProgressBar to show upload/download status.

<<file figure13-8.gif>>

The Code: FTPApp.java

see \Chapter13\5

import java.awt.*;

import java.awt.event.*;

import java.util.*;

import java.io.*;

import java.net.*;

import java.lang.reflect.*;

import sun.net.ftp.*;

import sun.net.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.event.*;

import dl.*;

public class FTPApp extends JFrame

{

  public static int BUFFER_SIZE = 10240;

  protected JTextField m_txtUser;

  protected JPasswordField m_txtPassword;

  protected JTextField m_txtURL;

  protected JTextField m_txtFile;

  protected JTextArea  m_monitor;

  protected JProgressBar m_progress;

  protected JButton m_btPut;

  protected JButton m_btGet;

  protected JButton m_btFile;

  protected JButton m_btClose;

  protected JFileChooser m_chooser;

  protected FtpClient m_client;

  protected String m_sLocalFile;

  protected String m_sHostFile;

  public FTPApp() {

    super("FTP Client");

    JPanel p = new JPanel();

    p.setLayout(new DialogLayout2(10, 5));

    p.setBorder(new EmptyBorder(5, 5, 5, 5));

    p.add(new JLabel("User name:"));

    m_txtUser = new JTextField("anonymous");

    p.add(m_txtUser);

    p.add(new JLabel("Password:"));

    m_txtPassword = new JPasswordField();

    p.add(m_txtPassword);

    p.add(new JLabel("URL:"));

    m_txtURL = new JTextField();

    p.add(m_txtURL);

    p.add(new JLabel("File:"));

    m_txtFile = new JTextField();

    p.add(m_txtFile);

    p.add(new DialogSeparator("Connection Monitor"));

    m_monitor = new JTextArea(5, 20);

    m_monitor.setEditable(false);

    JScrollPane ps = new JScrollPane(m_monitor);

    p.add(ps);

    m_progress = new JProgressBar();

    m_progress.setStringPainted(true);

    m_progress.setBorder(new BevelBorder(BevelBorder.LOWERED,

      Color.white, Color.gray));

    m_progress.setMinimum(0);

    JPanel p1 = new JPanel(new BorderLayout());

    p1.add(m_progress, BorderLayout.CENTER);

    p.add(p1);

    p.add(new DialogSeparator());

    m_btPut = new JButton("Put");

    ActionListener lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        if (connect()) {

          Thread uploader = new Thread() {

            public void run() {

              putFile();

              disconnect();

            }

          };

          uploader.start();

        }

      }

    };

    m_btPut.addActionListener(lst);
    m_btPut.setMnemonic('p');
    p.add(m_btPut);

    m_btGet = new JButton("Get");

    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        if (connect()) {

          Thread downloader = new Thread() {

            public void run() {

              getFile();

              disconnect();

            }

          };

          downloader.start();

        }

      }

    };

    m_btGet.addActionListener(lst);

    m_btGet.setMnemonic('g');

    p.add(m_btGet);

    m_btFile = new JButton("File");

    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        if (m_chooser.showSaveDialog(FTPApp.this) !=

          JFileChooser.APPROVE_OPTION)

            return;

        File f = m_chooser.getSelectedFile();

        m_txtFile.setText(f.getPath());

      }

    };

    m_btFile.addActionListener(lst);

    m_btFile.setMnemonic('f');

    p.add(m_btFile);

    m_btClose = new JButton("Close");

    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        if (m_client != null)

          disconnect();

        else

          System.exit(0);

      }

    };

    m_btClose.addActionListener(lst);

    m_btClose.setDefaultCapable(true);

    m_btClose.setMnemonic('g');

    p.add(m_btClose);

    getContentPane().add(p, BorderLayout.CENTER);

    m_chooser = new JFileChooser();

    m_chooser.setCurrentDirectory(new File("."));

    m_chooser.setApproveButtonToolTipText(

      "Select file for upload/download");

    WindowListener wndCloser = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        disconnect();

        System.exit(0);

      }

    };

    addWindowListener(wndCloser);

    setSize(340,340);

    setResizable(false);

    setVisible(true);

  }

  public void setButtonStates(boolean state) {

    m_btPut.setEnabled(state);

    m_btGet.setEnabled(state);

    m_btFile.setEnabled(state);

  }

  protected boolean connect() {

    m_monitor.setText("");

    setButtonStates(false);

    m_btClose.setText("Cancel");

    setCursor(Cursor.getPredefinedCursor(

      Cursor.WAIT_CURSOR));

    String user = m_txtUser.getText();

    if (user.length()==0) {

      message("Please enter user name");

      setButtonStates(true);

      return false;

    }

    String password = new String(m_txtPassword.getPassword());

    String sUrl = m_txtURL.getText();

    if (sUrl.length()==0) {

      message("Please enter URL");

      setButtonStates(true);

      return false;

    }

    m_sLocalFile = m_txtFile.getText();

    // Parse URL

    int index = sUrl.indexOf("//");

    if (index >= 0)

    sUrl = sUrl.substring(index+2);

    index = sUrl.indexOf("/");

    String host = sUrl.substring(0, index);

    sUrl = sUrl.substring(index+1);

    String sDir = "";

    index = sUrl.lastIndexOf("/");

    if (index >= 0) {

      sDir = sUrl.substring(0, index);

      sUrl = sUrl.substring(index+1);

    }

    m_sHostFile = sUrl;

    try {

      message("Connecting to host "+host);

      m_client = new FtpClient(host);

      m_client.login(user, password);

      message("User "+user+" login OK");

      message(m_client.welcomeMsg);

      m_client.cd(sDir);

      message("Directory: "+sDir);

      m_client.binary();

      return true;

    }

    catch (Exception ex) {

      message("Error: "+ex.toString());

      setButtonStates(true);

      return false;

    }

  }

  protected void disconnect() {

    if (m_client != null) {

      try { m_client.closeServer(); }

      catch (IOException ex) {}

      m_client = null;

    }

    Runnable runner = new Runnable() {

      public void run() {

        m_progress.setValue(0);

        setButtonStates(true);

        m_btClose.setText("Close");

        FTPApp.this.setCursor(Cursor.getPredefinedCursor(

          Cursor.DEFAULT_CURSOR));

      }

    };

    SwingUtilities.invokeLater(runner);

  }

  protected void getFile() {

    if (m_sLocalFile.length()==0) {

      m_sLocalFile = m_sHostFile;

      SwingUtilities.invokeLater( new Runnable() {

        public void run() {

          m_txtFile.setText(m_sLocalFile);

        }

      });

    }

    byte[] buffer = new byte[BUFFER_SIZE];

    try {

      int size = getFileSize(m_client, m_sHostFile);

      if (size > 0) {

        message("File " + m_sHostFile + ": " + size + " bytes");

        setProgressMaximum(size);

      }

      else

        message("File " + m_sHostFile + ": size unknown");

      FileOutputStream out = new

        FileOutputStream(m_sLocalFile);

      InputStream in = m_client.get(m_sHostFile);

      int counter = 0;

      while(true) {

        int bytes = in.read(buffer);

        if (bytes < 0)

          break;

        out.write(buffer, 0, bytes);

        counter += bytes;

        if (size > 0) {

          setProgressValue(counter);

          int proc = (int) Math.round(m_progress.

            getPercentComplete() * 100);

          setProgressString(proc + " %");

        }

        else {

          int kb = counter/1024;

          setProgressString(kb + " KB");

        }

      }

      out.close();

      in.close();

    }

    catch (Exception ex) {

      message("Error: "+ex.toString());

    }

  }

  protected void putFile() {

    if (m_sLocalFile.length()==0) {

      message("Please enter file name");

    }

    byte[] buffer = new byte[BUFFER_SIZE];

    try {

      File f = new File(m_sLocalFile);

      int size = (int)f.length();

      message("File " + m_sLocalFile + ": " + size + " bytes");

      setProgressMaximum (size);

      FileInputStream in = new

        FileInputStream(m_sLocalFile);

      OutputStream out = m_client.put(m_sHostFile);

      int counter = 0;

      while(true) {

        int bytes = in.read(buffer);

        if (bytes < 0)

          break;

        out.write(buffer, 0, bytes);

        counter += bytes;

        setProgressValue(counter);

        int proc = (int) Math.round(m_progress.

          getPercentComplete() * 100);

        setProgressString(proc + " %");

      }

      out.close();

      in.close();

    }

    catch (Exception ex) {

      message("Error: " + ex.toString());

    }

  }

  protected void message(final String str) {

    if (str != null) {

      Runnable runner = new Runnable() {

        public void run() {

          m_monitor.append(str + '\n');

          m_monitor.repaint();

        }

      };

      SwingUtilities.invokeLater(runner);

    }

  }

  protected void setProgressValue(final int value) {

    Runnable runner = new Runnable() {

      public void run() {

        m_progress.setValue(value);

      }

    };

    SwingUtilities.invokeLater(runner);

  }

  protected void setProgressMaximum(final int value) {

    Runnable runner = new Runnable() {

      public void run() {

        m_progress.setMaximum(value);

      }

    };

    SwingUtilities.invokeLater(runner);

  }

  protected void setProgressString(final String string) {

    Runnable runner = new Runnable() {

      public void run() {

        m_progress.setString(string);

      }

    };

    SwingUtilities.invokeLater(runner);

  }

  public static void main(String argv[]) {

    new FTPApp();

  }

  public static int getFileSize(FtpClient client, String fileName)

   throws IOException {

    TelnetInputStream lst = client.list();

    String str = "";

    fileName = fileName.toLowerCase();

    while(true) {

      int c = lst.read();

      char ch = (char) c;

      if (c < 0 || ch == '\n') {

        str = str.toLowerCase();

        if (str.indexOf(fileName) >= 0) {

          StringTokenizer tk = new StringTokenizer(str);

          int index = 0;

          while(tk.hasMoreTokens()) {

            String token = tk.nextToken();

            if (index == 4)

              try {

                return Integer.parseInt(token);

              }

              catch (NumberFormatException ex) {

                return -1;

              }

            index++;

          }

        }

        str = "";

      }

      if (c <= 0)

        break;

      str += ch;

    }

    return -1;

  }

}

Understanding the Code

Class FTPApp

Class variable:

int BUFFER_SIZE: the size of the buffer used for input/ouput operations.

Instance variables:

JTextField m_txtUser: login user name text field.

JPasswordField m_txtPassword: login password field.

JTextField m_txtURL: field for URL of file to be downloaded/uploaded on the remote site.

JTextField m_txtFile: field for file name of the file to be uploaded/downloaded on the local machine.

JTextArea m_monitor: used as a log to display various status messages.

JProgressBar m_progress: used to indicate the progress of an upload/download operation.

JButton m_btPut: used to initiate uploading.

JButton m_btGet: used to initiate downloading.

JButton m_btFile: used to bring up a file chooser dialog to choose a local file or specify a file name and location.

JButton m_btClose: used to close the application.

JFileChooser m_chooser: used to choose a local file or specify a file name and location.

FtpClient m_client: client connection to host which manages I/O operations.

String m_sLocalFile: name of the most recent local file involved in a data transfer.

String m_sHostFile: name of the most recent host file involved in a data transfer.

The FTPApp constructor first creates a panel using a DialogLayout2 layout manager, and instantiates and adds our four text fields with corresponding labels (recall that our DialogLayout2 manager requires that label/input field paris are added in the specific label1, field1, label2, field2, etc., order). The m_monitor text area is created and placed in a JScrollPane, and separated from the label/field panel (by an instance of our custom DialogSeparator class titled "Connection Monitor"). The m_progress JProgressBar is created and placed in a JPanel with a BorderLayout to ensure that DialogLayout2 allows it to occupy the maximum width across the frame, as well as its preferred height).

A plain DialogSeparator is added below the progress bar, and our four buttons are created with attached ActionListeners,  and added to the DialogLayout2 panel resulting in a horizontal row at the bottom of the frame. The button titled "Put" attempts to connect to a host using our connect() method. If it is successful a new thread is started which calls putFile() to upload a selected file, and then calls disconnect() to terminate the connection to the host. Similarly, the button titled "Get" attempts to connect to a host, and, if successful, starts a thread which calls our getFile() method to download a file, and then disconnect(). The button titled "File" brings up our JFileChooser dialog to select a local file or specify a new file name and location. The button titled "Close" invokes disconnect() to terminate a connection to the host if FTP transfer is in progress (i.e. if m_client instance is not null). If a transfer is not in progress  the application is terminated.

The setButtonStates() method takes a boolean parameter and enables/disables the "Put," "Get," and "File" buttons accordingly.

The connect() method establishes a connection to the remote host and returns true in the case of success, or false otherwise. First this method disables our "Put," "Get," and "File" push buttons, and sets the text of the last button to "Cancel." Then this method reads the content of the text fields to obtain login name, password, URL, and local file name. The URL is parsed and split into the host name, remote directory, and the host file name. A new FtpClient instance is created to connect to the remote host and stored in our m_client instance variable. Then the login() method is invoked on m_client to login to the server using the specified user name and password. If the login is successful we change the remote directory and set the connection type to binary (which is almost always required for file transfers). If no exceptions have been thrown during this process, connect() returns true. Otherwise it shows an exception in our m_monitor text area, re-enables our buttons, and returns false.

Note: The connect() method code involving connecting to a host and changing directory would be better off in a separate thread, and we suggest this enhancement for more professional implementations. All other time-intensive code in this example is executed in separate threads.

The disconnect() method invokes closeServer() on the current m_client FTPClient instance if it is in use. It then sets the m_client reference to null, allowing garbage collection of the FTPClient object. This method also clears the progress bar component, enables all push buttons which may have been disabled by the connect() method, and restores the text of the "Close" button. Note that all component updates are wrapped in a Runnable and sent to the event-dispatching queue with SwingUtilities.invokeLater().

The getFile() method downloads a prespecified file from the current host. If the name of the destination local file is not specified, the name of the remote file is used. This method tries to determine the size of the remote file by calling our getFileSize() helper method (see below). If that succeeds, the file size is set as the maximum value of the progress bar (the minimum value is always 0) with our custom setProgressMaximum() method. Then a FileOutputStream is openned to write to the local file, and an InputStream is retrieved from the FTPClient to read from the remote file. A while loop is set up to perform typical read/write operations until all content of the remote file is written to the local file. During this process the number of bytes read is accumulated in the counter local variable. If the size of the file is known, this number is assigned to the progress bar using our custom setProgressValue() method. We also calculate the percentage of downloading complete with our custom getPercentComplete() method, and display it in the progress bar using our custom setProgressString() method. If the size of file is unknown (i.e. it is less than or equal to 0), we can only display the number of kilobytes currently downloaded at any given time. To obtain this value we simply divide the current byte count, stored in the local counter variable, by 1024.

The putFile() method uploads the content of a local file to a remote pre-specified URL. If the name of the local file is not specified, a message is printed, using our custom message() method, and we simply return. Otherwise, the size of the local file is determined and used as the maximum value of our progress bar using our custom setMaximum() method (the minimum value is always 0). A FileInputStream is openned to read from the local file, and an OutputStream is retrieved from the FTPClient to write to the remote file. A while loop is set up to perform typical read/write operations until all content of the local file is written to the remote host. During this process the number of bytes written is accumulated in the counter local variable. This number is assigned to the progress bar using our custom setProgressValue() method. As in the getFile() method, we also calculate the percentage of downloading complete with our custom getPercentComplete() method, and display it in the progress bar using our custom setProgressString() method. Since we can always determine the size of a local File object, there is no need to display the progress in terms of kilobytes (as we did in getFile() above).

The message() method takes a String parameter to display in our m_monitor text area. The setProgressValue() and setProgressMaximum() methods assign selected and maximum values to our progress bar respectively. Since each of these methods modifes the state of our progress bar component, and each is called from a custom thread, we wrap their bodies in Runnables and send them to the event-dispatching queue using SwingUtilities.invokeLater().

Unfortunately the FtpClient class does not provide a direct way to determine the size of a remote file, as well as any other available file specifics. The only way we can get any information about files on the remote host using this class is to call the its list() method which returns a TelnetInputStream supplying the printout of the results of an ls -l command. Our getFileSize() method uses this method in an attempt to obtain the length of a remote file specified by a given file name and FTPClient instance. This method captures the printout from the remote server, splits it into lines separated by '\n' characters, and uses a StringTokenizer to parse them into tokens. According to the syntax of the ls -l command output, the the length of the file in bytes appears as the 5th token, and the last token should contain the file name. So we go character by character through each line until a line containing a matching file name is found, and the length is returned to the caller. If this does not succeed we return -1 to indicate that the server either does not allow browsing of its content or that an error has occured.

Running the Code

Figure 13.8 shows FTPApp in action. Try running this application and transfering a few files. Start by entering your user name, password, URL containing the host FTP sever, and (optionally) a local file name and path to act as the source or destination of a transfer. Press the "Get" button to download a specified remote file, or press the "Put" button to upload a specified local file to the host. If the required connection is established successfully, you will see the transfer progress updated incrementally in the progress bar.

In figure 13.8 we specify "anonymous" as username and use an email address as password. In our URL text field we specify the remote "tutorial.zip" file (the most recent Java Tutorial) on the "java.sun.com" FTP server in its "docs" directory. In our File text field we specify "tutorial.zip" as the destination file in the current running directory. Clicking on "Get" establishes a connection, changes remote directory to "docs," determines the size of the remote "tutorial.zip" file, and starts retrieving and storing it as a local file in the current running directory. Try performing this transfer and note how smoothly the progress bar updates itself (it can't hurt to keep a local copy of the Java Tutorial, but be aware that this archive is close to 10 megabytes).

Note: In the next chapter we will customize JFileChooser to build a ZIP/JAR archive tool. This can be used to unpackage tutorial.zip if you do not have access to an equivalent tool.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy