diff --git a/java/common/build.xml b/java/common/build.xml deleted file mode 100644 index 6dd2a885..00000000 --- a/java/common/build.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/java/common/src/com/google/airbag/common/AbstractMinidumpServlet.java b/java/common/src/com/google/airbag/common/AbstractMinidumpServlet.java deleted file mode 100644 index 27fae88c..00000000 --- a/java/common/src/com/google/airbag/common/AbstractMinidumpServlet.java +++ /dev/null @@ -1,135 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.IOException; -import java.io.InputStream; -import java.text.NumberFormat; -import java.util.Calendar; -import java.util.Random; -import java.util.SortedMap; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * Common part of uploading a file with parameters. A subclass needs to - * provide a ReportStorage implementation. - * - * An upload file is saved in the file storage with parsed parameters from - * the URL. - * - * A separate processor will access the file storage and process these - * reports. - */ -@SuppressWarnings("serial") -public class AbstractMinidumpServlet extends HttpServlet { - // Minidump storage to be instantiated by subclasses in init() method. - protected ReportStorage minidumpStorage; - - // Random number generator - private final Random random = new Random(); - - /** - * Always return success to a GET, to keep out nosy people who go - * directly to the URL. - */ - public void doGet(HttpServletRequest req, HttpServletResponse res) { - res.setStatus(HttpServletResponse.SC_OK); - } - - /** - * Takes the file POSTed to the server and writes it to a file storage. - * Parameters in URL are saved as attributes of the file. - * - * @param req a wrapped HttpServletRequest that represents a multipart - * request - * @return unique ID for this report, can be used to get parameters and - * uploaded file contents from a file storage. If these is a - * collation, returns null. - * - * @throws ServletException - * @throws IOException - */ - protected String saveFile(MultipartRequest req) - throws ServletException, IOException { - // parse mutilpart request - SortedMap params = req.getParameters(); - - //TODO(fqian): verify required fields of a report - InputStream inputs = req.getInputStream(); - - /* It is possible that two or more clients report crashes at the same - * time with same parameters. To reduce the chance of collation, we - * add two internal parameters: - * 1. reporting time, a time string in the form of YYMMDD-HHMMSS; - * 2. a random number; - * - * In theory, there is still a chance to collate, but it is very low. - * When collation happens, the one coming later is dropped. - */ - // 1. add a timestamp to parameters - params.put(NameConstants.REPORTTIME_PNAME, currentTimeString()); - - // 2. get a random number to make the change of collation very small - int r; - synchronized (this.random) { - r = this.random.nextInt(); - } - params.put(NameConstants.RANDOMNUM_PNAME, Integer.toString(r)); - - String fid = this.minidumpStorage.getUniqueId(params); - - assert fid != null; - - if (this.minidumpStorage.reportExists(fid)) { - // collation happens - return null; - } - - this.minidumpStorage.saveAttributes(fid, params); - // save uploaded contents to the storage - this.minidumpStorage.writeStreamToReport(fid, inputs, 0); - - return fid; - } - - /* Gets a string representing the current time using the format: - * YYMMDD-HHMMSS. - */ - private String currentTimeString() { - NumberFormat formatter = NumberFormat.getInstance(); - formatter.setGroupingUsed(false); - formatter.setMinimumIntegerDigits(2); // 2 digits per date component - - // All servers are in Pacific time - Calendar cal = Calendar.getInstance(); - - StringBuffer tstring = new StringBuffer(); - tstring.append(formatter.format(cal.get(Calendar.YEAR))); - // We want January = 1. - tstring.append(formatter.format((cal.get(Calendar.MONTH) + 1))); - tstring.append(formatter.format(cal.get(Calendar.DAY_OF_MONTH))); - tstring.append("-"); - tstring.append(formatter.format(cal.get(Calendar.HOUR_OF_DAY))); - tstring.append(formatter.format(cal.get(Calendar.MINUTE))); - tstring.append(formatter.format(cal.get(Calendar.SECOND))); - - return new String(tstring); - } -} diff --git a/java/common/src/com/google/airbag/common/CrashUtils.java b/java/common/src/com/google/airbag/common/CrashUtils.java deleted file mode 100644 index a010c526..00000000 --- a/java/common/src/com/google/airbag/common/CrashUtils.java +++ /dev/null @@ -1,188 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -/** A utility class used by crash reporting server. */ - -public class CrashUtils { - // A map from numbers to hexadecimal characters. - private static final char[] maps = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', - }; - - private static final String MD_ALGORITHM = "MD5"; - - // delimiter between record - private static final String RECORD_DELIMITER = ";"; - private static final String FIELD_DELIMITER = " "; - private static final int BUFFER_SIZE = 1024; - - /** - * Given a byte array, returns a string of its hex representation. - * Each byte is represented by two characters for its high and low - * parts. For example, if the input is [0x3F, 0x01], this method - * returns 3F01. - * - * @param bs a byte array - * @return a string of hexadecimal characters - */ - public static String bytesToHexString(byte[] bs) { - StringBuffer sb = new StringBuffer(); - for (byte b : bs) { - int high = (b >> 4) & 0x0F; - int low = b & 0x0F; - sb.append(maps[high]); - sb.append(maps[low]); - } - return sb.toString(); - } - - /** - * Given a byte array, computes its message digest using one-way hash - * functions. - * - * @param data a byte array - * @return a string as its signature, or null if no message digest - * algorithm - * supported by the system. - */ - public static String dataSignature(byte[] data) { - try { - MessageDigest md = MessageDigest.getInstance(MD_ALGORITHM); - return bytesToHexString(md.digest(data)); - } catch (NoSuchAlgorithmException e) { - return null; - } - } - - /** - * Compute the signature of a file by calling dataSignature on file - * contents. - * - * @param file a file name which signature to be computed - * @return the method signature of the file, or null if failed to - * read file contents, or message digest algorithm is not supported - */ - public static String fileSignature(File file) { - try { - FileInputStream fis = new FileInputStream(file); - byte[] buf = new byte[BUFFER_SIZE]; - MessageDigest md = MessageDigest.getInstance(MD_ALGORITHM); - while (true) { - int bytesRead = fis.read(buf, 0, BUFFER_SIZE); - if (bytesRead == -1) - break; - md.update(buf, 0, bytesRead); - } - return bytesToHexString(md.digest()); - - } catch (NoSuchAlgorithmException e) { - return null; - } catch (IOException e) { - return null; - } - } - - /** - * Encodes an attribute map to a string. Encoded string format: - * name value1[ value2];name value1[ value2] - * Names and values should be escaped so that there are no - * RECORD_DELIMITER and VALUE_DELIMITER in strings. - * - * @param attributes a maps of attributes name and value to be encoded - * @return a string of encoded attributes - */ - public static - String attributesToString(SortedMap attributes) { - StringBuffer res = new StringBuffer(); - for (Map.Entry e : attributes.entrySet()) { - String name = e.getKey(); - String value = e.getValue(); - - assert name.indexOf(RECORD_DELIMITER) == -1; - assert name.indexOf(FIELD_DELIMITER) == -1; - res.append(name).append(FIELD_DELIMITER); - - assert value.indexOf(RECORD_DELIMITER) == -1; - assert value.indexOf(FIELD_DELIMITER) == -1; - res.append(value).append(RECORD_DELIMITER); - } - return new String(res); - } - - /** - * Decodes a string to a map of attributes. - */ - public static SortedMap stringToAttributes(String s) { - SortedMap map = - new TreeMap(); - String[] records = s.split(RECORD_DELIMITER); - for (String r : records) { - String[] fields = r.trim().split(FIELD_DELIMITER); - if (fields.length != 2) // discard records that has no values - continue; - String name = fields[0].trim(); - String value = fields[1].trim(); - map.put(name, value); - } - return map; - } - - /** - * Copies bytes from an input stream to an output stream, with max bytes. - * - * @param ins an input stream to read - * @param outs an output stream to write - * @param max the maximum number of bytes to copy. If max <= 0, copy bytes - * until the end of input stream - * @return the number of bytes copied - * @throws IOException - */ - public static int copyStream(InputStream ins, OutputStream outs, int max) - throws IOException { - byte[] buf = new byte[BUFFER_SIZE]; - int bytesWritten = 0; - - while (true) { - int bytesToRead = BUFFER_SIZE; - if (max > 0) - bytesToRead = Math.min(BUFFER_SIZE, max - bytesWritten); - - if (bytesToRead <= 0) - break; - - int bytesRead = ins.read(buf, 0, bytesToRead); - if (bytesRead == -1) // end of input stream - break; - outs.write(buf, 0, bytesRead); - bytesWritten += bytesRead; - } - - return bytesWritten; - } -} diff --git a/java/common/src/com/google/airbag/common/MultipartRequest.java b/java/common/src/com/google/airbag/common/MultipartRequest.java deleted file mode 100644 index bd737bfb..00000000 --- a/java/common/src/com/google/airbag/common/MultipartRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.util.SortedMap; -import java.io.InputStream; - -/** - * A common interface for different multipart HttpServletRequest - * implementations. The interface is simple enough to be used by the - * upload server. - */ - -public interface MultipartRequest { - /** - * Returns a sorted map of name to values of an HTTP request. - */ - public SortedMap getParameters(); - - /** - * Returns an input stream of uploading file. - */ - public InputStream getInputStream(); -} diff --git a/java/common/src/com/google/airbag/common/NameConstants.java b/java/common/src/com/google/airbag/common/NameConstants.java deleted file mode 100644 index 6ea80692..00000000 --- a/java/common/src/com/google/airbag/common/NameConstants.java +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -/** A class defines URL parameter names. */ - -public class NameConstants { - // URL parameter names - // product name - public static final String PRODUCT_PNAME = "prod"; - // version - public static final String VERSION_PNAME = "ver"; - // application or module - public static final String APPLICATION_PNAME = "app"; - // platform, e.g., win32, linux, mac - public static final String PLATFORM_PNAME = "plat"; - // report format, e.g., dump, xml - public static final String FORMAT_PNAME = "fmt"; - // process uptime - public static final String PROCESSUPTIME_PNAME = "procup"; - // cumulative process uptime - public static final String CUMULATIVEUPTIME_PNAME = "cumup"; - // time when report is created - public static final String REPORTTIME_PNAME = "rept"; - // a random number - public static final String RANDOMNUM_PNAME = "rand"; - // report checksum - public static final String CHECKSUM_PNAME = "sum"; -} diff --git a/java/common/src/com/google/airbag/common/ReportQueue.java b/java/common/src/com/google/airbag/common/ReportQueue.java deleted file mode 100644 index c92f385e..00000000 --- a/java/common/src/com/google/airbag/common/ReportQueue.java +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.util.LinkedList; - -/** - * A queue interface for unprocessed report ids. The interface is intended - * for inter-process usage. A report uploading server enqueues new report - * ids, and a processor dequeues ids. - * - * The interface is much simpler than java.util.Queue. An implementation - * should provide a persistent storage of queued ids even when a process - * is killed. - */ - -public interface ReportQueue { - /** - * Enqueue a record id. - * - * @param rid - * @return true if success - */ - public boolean enqueue(String rid); - - /** - * Enqueue a list of ids - * - * @param ids - * @return true if success - */ - public boolean enqueue(LinkedList ids); - - /** - * Checks if a queue is empty - * @return true if the queue is empty - */ - public boolean empty(); - - /** - * Removes several ids from the queue. An implementation decides how - * many ids to be removed. - * - * @return a list of queue - */ - public LinkedList dequeue(); -} diff --git a/java/common/src/com/google/airbag/common/ReportQueueDirImpl.java b/java/common/src/com/google/airbag/common/ReportQueueDirImpl.java deleted file mode 100644 index dc7c922a..00000000 --- a/java/common/src/com/google/airbag/common/ReportQueueDirImpl.java +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.util.LinkedList; -import java.io.File; -import java.io.IOException; - -/** - * Implements ReportQueue using directories. Some restrictions: - *
    - *
  • Ids must be valid file names;
  • - *
  • Ids cannot be duplicated, a duplicated id is ignored;
  • - *
  • No guarantees on ordering, in other words, this is not really - * a queue (with FIFO order);
  • - *
- */ - -public class ReportQueueDirImpl implements ReportQueue { - // maximum number of ids returned by dequque method. - private static final int MAX_DEQUEUED_IDS = 100; - - // the directory name for storing files - private String queueDir; - - /** - * Creates an instance of ReportQueueDirImpl with a directory name. - * @param dirname - */ - public ReportQueueDirImpl(String dirname) - throws IOException - { - this.queueDir = dirname; - File q = new File(dirname); - if (!q.exists()) - q.mkdirs(); - - if (!q.isDirectory()) - throw new IOException("name "+dirname - +" exits already, but not a directory."); - } - - /** Enqueue a report id. */ - public boolean enqueue(String rid) { - //lock on the directory - // add a file named by id - File f = new File(this.queueDir, rid); - try { - return f.createNewFile(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return false; - } - } - - /** Enqueue a list of ids. */ - public boolean enqueue(LinkedList ids) { - //lock on the directory - // add a file named by id - for (String rid : ids) { - File f = new File(this.queueDir, rid); - try { - if (!f.createNewFile()) - return false; - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return false; - } - } - return true; - } - - /** Checks if the queue is empty. */ - public boolean empty() { - File f = new File(this.queueDir); - String[] ids = f.list(); - if (ids == null) - return true; - else - return ids.length == 0; - } - - /** Remove ids from the queue. */ - public LinkedList dequeue() { - // lock on the directory - LinkedList rids = new LinkedList(); - File d = new File(this.queueDir); - String[] ids = d.list(); - if (ids == null) - return rids; - - for (int i =0; i < Math.min(ids.length, MAX_DEQUEUED_IDS); i++) { - File f = new File(this.queueDir, ids[i]); - f.delete(); - rids.add(ids[i]); - } - - return rids; - } -} diff --git a/java/common/src/com/google/airbag/common/ReportQueueFileImpl.java b/java/common/src/com/google/airbag/common/ReportQueueFileImpl.java deleted file mode 100644 index 8d858664..00000000 --- a/java/common/src/com/google/airbag/common/ReportQueueFileImpl.java +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.util.LinkedList; -import java.util.logging.Logger; - -/** - * Implementation of ReportQueue using a shared file. File accesses are - * protected by a file lock. When dequeue, all reports in the file were - * returned and the file is truncated to length zero. - */ - -public class ReportQueueFileImpl implements ReportQueue { - private static final Logger logger = - Logger.getLogger(ReportQueueFileImpl.class.getName()); - private String queueFile; - - /** Given a file name, creates an instance of ReportQueueFileImpl. */ - public ReportQueueFileImpl(String fname) { - this.queueFile = fname; - } - - /** Enqueues a report id. */ - public boolean enqueue(String rid) { - try { - RandomAccessFile raf = new RandomAccessFile(this.queueFile, "rw"); - FileChannel fc = raf.getChannel(); - // block thread until lock is obtained - FileLock lock = fc.lock(); - raf.seek(raf.length()); - raf.writeBytes(rid); - raf.writeByte('\n'); - lock.release(); - fc.close(); - raf.close(); - return true; - } catch (FileNotFoundException e) { - logger.severe("Cannot open file "+this.queueFile+" for write."); - } catch (IOException e) { - logger.severe("Cannot write to file "+this.queueFile); - } - return false; - } - - /** Enqueues a list of report ids. */ - public boolean enqueue(LinkedList ids) { - try { - RandomAccessFile raf = new RandomAccessFile(this.queueFile, "rw"); - FileChannel fc = raf.getChannel(); - // block thread until lock is obtained - FileLock lock = fc.lock(); - raf.seek(raf.length()); - for (String rid : ids) { - raf.writeBytes(rid); - raf.writeByte('\n'); - } - lock.release(); - fc.close(); - raf.close(); - return true; - } catch (FileNotFoundException e) { - logger.severe("Cannot open file "+this.queueFile+" for write."); - } catch (IOException e) { - logger.severe("Cannot write to file "+this.queueFile); - } - return false; - } - - public boolean empty() { - // check the length of the file - File f = new File(this.queueFile); - return f.length() == 0L; - } - - public LinkedList dequeue() { - LinkedList ids = new LinkedList(); - - try { - RandomAccessFile raf = new RandomAccessFile(this.queueFile, "rw"); - FileChannel fc = raf.getChannel(); - FileLock flock = fc.lock(); - - while (true) { - String s = raf.readLine(); - if (s == null) - break; - s = s.trim(); - if (s.equals("")) - continue; - - ids.add(s); - } - - fc.truncate(0L); - - // release the lock - flock.release(); - fc.close(); - raf.close(); - } catch (FileNotFoundException e) { - logger.severe("Cannot open file "+this.queueFile+" for write."); - } catch (IOException e) { - logger.severe("Cannot write to file "+this.queueFile); - } - return ids; - } -} diff --git a/java/common/src/com/google/airbag/common/ReportQueueTest.java b/java/common/src/com/google/airbag/common/ReportQueueTest.java deleted file mode 100644 index 62f156ea..00000000 --- a/java/common/src/com/google/airbag/common/ReportQueueTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.IOException; -import java.util.LinkedList; - -/** - * A test class for ReportQueue implementations, currently tests - * ReportQueueDirImpl and ReportQueueFileImpl. - */ -public class ReportQueueTest { - - public static void main(String[] args) { - ReportQueue rq = new ReportQueueFileImpl("/tmp/rqtest"); - runTest(rq); - - try { - rq = new ReportQueueDirImpl("/tmp/rqdir"); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - runTest(rq); - - System.out.println("OK"); - } - - private static void runTest(ReportQueue rq) { - rq.enqueue("hello"); - rq.enqueue("world"); - - LinkedList v = rq.dequeue(); - assert v.size() == 2; - - assert v.get(0).equals("hello"); - assert v.get(1).equals("world"); - assert rq.empty(); - - v.remove(); - - rq.enqueue(v); - assert !rq.empty(); - - v = rq.dequeue(); - assert v.size() == 1; - assert v.get(0).equals("world"); - } -} diff --git a/java/common/src/com/google/airbag/common/ReportStorage.java b/java/common/src/com/google/airbag/common/ReportStorage.java deleted file mode 100644 index 0c01281c..00000000 --- a/java/common/src/com/google/airbag/common/ReportStorage.java +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.InputStream; -import java.io.IOException; -import java.io.FileNotFoundException; - -import java.util.SortedMap; - -/** - * ReportStorage.java - * - *

- * Provide an abstract layer for storing crash reports and associated meta - * data. - *

- * - *

- * The interface is intended to be used by a client in the following way: - * - *

- *     ReportStorage rs = new ReportStorageImpl(...);
- *     // Write an uploading file to a storage
- *     try {
- *        String rid = rs.getUniqueId(attributes); // params from URL
- *        rs.saveAttributes(id, attributes);
- *        if (!rs.reportExists(rid) || allowOverwrite)
- *           rs.writeStreamToReport(rid, input, 0);
- *        else
- *           rs.appendStreamToReport(rid, input, 0);
- *     } catch (...)
- *  
- *     // Read a file from the storage
- *     try {
- *        OutputStream os = fs.openReportForRead(fid);
- *        os.read(...);
- *        os.close();
- *     } catch (...)
- * 
- */ - -public interface ReportStorage { - /** - * Given a sorted map of attributes (name and value), returns a unique id - * of the crash report. - * - * @param params a sorted map from name to value - * @return a string as the file id - */ - public String getUniqueId(SortedMap params); - - /** - * Given a report id, checks if the report data AND attributes identified - * by this id exists on the storage. - * - * @param id a report id - * @return true if the id represents an existing file - */ - public boolean reportExists(String id); - - /** - * Given a report id and a sorted map of attributes, saves attributes on - * the storage. - * - * @param id a report id - * @param attrs attributes associated with this id - * @return true if attributes are saved successfully - */ - public boolean saveAttributes(String id, SortedMap attrs); - - /** - * Given a report id, returns attributes associated with this report. - * - * @param id a report id - * @return a sorted map from name to value - * @throws FileNotFoundException if fileExists(id) returns false - */ - public SortedMap getAttributes(String id) - throws FileNotFoundException; - - /** - * Writes max bytes from an input stream to a report identified by - * an id. - * - * @param id a report id - * @param input an input stream - * @param max - * maximum bytes to be written, if max is less or equal than 0, it will - * write all bytes from input stream to the file - * @return the number of bytes written - * @throws FileNotFoundException - * if reportExists(id) returns false - * @throws IOException - */ - public int writeStreamToReport(String id, InputStream input, int max) - throws FileNotFoundException, IOException; - - /** - * Opens a report for read. - * - * @param id a report id - * @return an output stream for read - * @throws FileNotFoundException - */ - public InputStream openReportForRead(String id) throws FileNotFoundException; - - /** - * @param id a report id - * @return the checksum of a report. - * @throws FileNotFoundException - */ - public String getChecksum(String id) throws FileNotFoundException; - - /** - * Removes a report. - * - * @param id a report id - * @return true if the report is removed successfully - */ - public boolean removeReport(String id) throws FileNotFoundException; -} diff --git a/java/common/src/com/google/airbag/common/ReportStorageLocalFileImpl.java b/java/common/src/com/google/airbag/common/ReportStorageLocalFileImpl.java deleted file mode 100644 index 9a6cf91f..00000000 --- a/java/common/src/com/google/airbag/common/ReportStorageLocalFileImpl.java +++ /dev/null @@ -1,197 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.util.SortedMap; -import java.util.logging.Logger; - -import java.io.File; -import java.io.InputStream; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.FileNotFoundException; - -/** - *

Implement FileStorage using a local file system.

- * - *

Given a sorted map of attributes, create a checksum as unique file - * id.

- * - *

Each file id is associated with two files in the storage: - *

    - *
  1. an attribute file named as .attr;
  2. - *
  3. a data file named as .data;
  4. - *
- *

- */ - -public class ReportStorageLocalFileImpl implements ReportStorage { - - // Name extension of attribute file - private static final String ATTR_FILE_EXT = ".attr"; - - // Name extension of data file - private static final String DATA_FILE_EXT = ".data"; - - // Set the maximum file length at 1M - private static final long MAX_ATTR_FILE_LENGTH = 1 << 10; - - // Logging - private static final Logger logger = - Logger.getLogger(ReportStorageLocalFileImpl.class.getName()); - - // Directory name for storing files. - private String directoryName; - - /** - * Creates an instance of ReportStorageLocalFileImpl by providing a - * directory name. - * - * @param dirname a directory for storing files - * @throws IOException - * @throws NullPointerException if dirname is null - */ - public ReportStorageLocalFileImpl(String dirname) throws IOException { - this.directoryName = dirname; - // new File can throw NullPointerException if dirname is null - File dir = new File(dirname); - if (!dir.exists() && !dir.mkdirs()) - throw new IOException("Cannot make dirs for "+dirname); - - if (!dir.canWrite()) - throw new IOException("Cannot write to "+dirname - +", check your permissions."); - } - - /** - * Returns a hashed string of attributes. Attributes are saved - * in the file storage if not exists. - */ - public String getUniqueId(SortedMap attributes) - { - String attr = CrashUtils.attributesToString(attributes); - return CrashUtils.dataSignature(attr.getBytes()); - } - - /** - * Saves attributes associated with a given id. if attributes does not - * match the id (comparing results of getUniqueId(attributes) - * with id), it returns false. Otherwise, attributes are saved. - */ - public boolean saveAttributes(String id, SortedMap attributes) - { - String attr = CrashUtils.attributesToString(attributes); - String digest = CrashUtils.dataSignature(attr.getBytes()); - - if (!digest.equals(id)) - return false; - - try { - File attrFile = new File(this.directoryName, digest+ATTR_FILE_EXT); - - // check if attr file exists - if (!attrFile.exists()) { - FileOutputStream fos = new FileOutputStream(attrFile); - fos.write(attr.getBytes()); - fos.close(); - } - - return true; - } catch (IOException e) { - e.printStackTrace(); - logger.warning(e.toString()); - return false; - } - } - - /** Checks if a report id exists. */ - public boolean reportExists(String id) { - File datafile = new File(this.directoryName, id+DATA_FILE_EXT); - File attrfile = new File(this.directoryName, id+ATTR_FILE_EXT); - return datafile.isFile() && attrfile.isFile(); - } - - /** Returns attributes in a map associated with an id. */ - public SortedMap getAttributes(String id) - throws FileNotFoundException - { - if (!this.reportExists(id)) - throw new FileNotFoundException("no file is identified by "+id); - - File attrfile = new File(this.directoryName, id+ATTR_FILE_EXT); - if (!attrfile.isFile()) - throw new FileNotFoundException("no file is identified by "+id); - - int length = (int) attrfile.length(); - if (length >= MAX_ATTR_FILE_LENGTH) - throw new FileNotFoundException("no file is identified by "+id); - - byte[] content = new byte[length]; - - try { - FileInputStream fis = new FileInputStream(attrfile); - fis.read(content); - fis.close(); - - // verify checksum - String sig = CrashUtils.dataSignature(content); - if (!sig.equals(id)) { - logger.warning("illegal access to "+attrfile); - return null; - } - - // parse contents - return CrashUtils.stringToAttributes(new String(content)); - - } catch (IOException e) { - logger.warning(e.toString()); - return null; - } - } - - public int writeStreamToReport(String id, InputStream input, int max) - throws FileNotFoundException, IOException { - File datafile = new File(this.directoryName, id + DATA_FILE_EXT); - FileOutputStream fos = new FileOutputStream(datafile); - - int bytesCopied = CrashUtils.copyStream(input, fos, max); - - fos.close(); - - return bytesCopied; - } - - public InputStream openReportForRead(String id) throws FileNotFoundException { - File datafile = new File(this.directoryName, id + DATA_FILE_EXT); - return new FileInputStream(datafile); - } - - public String getChecksum(String id) throws FileNotFoundException { - File datafile = new File(this.directoryName, id + DATA_FILE_EXT); - return CrashUtils.fileSignature(datafile); - } - - - public boolean removeReport(String id) { - File datafile = new File(this.directoryName, id + DATA_FILE_EXT); - File attrfile = new File(this.directoryName, id + ATTR_FILE_EXT); - - datafile.delete(); - attrfile.delete(); - - return true; - } -} diff --git a/java/common/src/com/google/airbag/common/ReportStorageTest.java b/java/common/src/com/google/airbag/common/ReportStorageTest.java deleted file mode 100644 index dc17f56a..00000000 --- a/java/common/src/com/google/airbag/common/ReportStorageTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.TreeMap; - -/** A simple regression test of ReportStorage.java and implementations. - */ - -public class ReportStorageTest { - public static void main(String[] args) { - // use /tmp/test as testing directory - ReportStorage rs = null; - try { - rs = new ReportStorageLocalFileImpl("/tmp/test"); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - - runTest(rs); - - // test passed - System.out.println("OK."); - } - - private static void runTest(ReportStorage rs) { - // test CrashUtil.bytesToHexString - byte[] ba = new byte[4]; - ba[0] = (byte)0xFF; - ba[1] = (byte)0x00; - ba[2] = (byte)0x0F; - ba[3] = (byte)0xF0; - String s = CrashUtils.bytesToHexString(ba); - assert s.equals("FF000FF0"); - - // construct a simple map of attributes - TreeMap params = - new TreeMap(); - params.put("Hello", "World"); - String rid = rs.getUniqueId(params); - assert rid != null; - - boolean b = rs.saveAttributes(rid, params); - assert b; - ba = "hellow, world!".getBytes(); - InputStream in = new ByteArrayInputStream(ba); - - assert rs.reportExists(rid); - - // save contents to storage - try { - rs.writeStreamToReport(rid, in, 0); - } catch (FileNotFoundException e) { - e.printStackTrace(); - System.exit(1); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - - // read contents out - try { - InputStream in1 = rs.openReportForRead(rid); - assert in1.available() == ba.length; - in1.read(ba); - assert(new String(ba).equals("hellow, world!")); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - } -} diff --git a/java/common/src/com/google/airbag/common/SymbolStorage.java b/java/common/src/com/google/airbag/common/SymbolStorage.java deleted file mode 100644 index 5f0a3508..00000000 --- a/java/common/src/com/google/airbag/common/SymbolStorage.java +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.IOException; -import java.io.InputStream; -import java.util.SortedMap; - -/** - *

SymbolStorage provides a simple interface for storing and retrieving - * symbol files. Symbol files are indexed by a set of attributes: - *

    - *
  • product name
  • - *
  • version (build id)
  • - *
  • platform
  • - *
  • application/module name
  • - *
- *

- * - *

- * The interface is designed for a symbol server supports upload, getid, - * download operations. - *

- */ - -public interface SymbolStorage { - /** - * Saves a symbol file indexed by a set of attributes. Attributes include - * product, version, platform, application/module, plus a checksum of - * the symbol file. - * - * If a symbol file whose checksum matches the attribute, the input stream - * can be NULL. No contents will be written. This can save some workloads - * of uploading symbols. - * - * @param attrs a map of attributes, it must have 'prod', 'ver', 'plat', - * 'app', and 'sum', values of the first four attributes are used - * as index, and the value of the last attribute is the MD5 checksum - * of the symbol file for verification. - * @param contents symbol file contents. - * @return true if checksum matches - * @throws IOException - */ - public boolean saveSymbolFile(SortedMap attrs, - InputStream contents) throws IOException; - - /** - * Gets the checksum of a symbol file indexed by a set of attributes. - * @param attrs a map of attributes, must include 'prod', 'ver', 'plat', - * and 'app'. - * @return MD5 checksum as a string - * @throws IOException - */ - public String getFileChecksum(SortedMap attrs) - throws IOException; - - /** - * Checks if a file exists already (identified by its checksum). - * @param checksum the file checksum - * @return true if a file with the same checksum exists - */ - public boolean fileExists(String checksum); - - /** - * Gets an input stream of a symbol server indexed by a set of attributes. - * @param attrs a map of attributes, must include 'prod', 'ver', 'plat', - * and 'app'. - * @return an input stream of the symbol file - * @throws IOException - */ - public InputStream openFileForRead(SortedMap attrs) - throws IOException; -} diff --git a/java/common/src/com/google/airbag/common/SymbolStorageLocalFileImpl.java b/java/common/src/com/google/airbag/common/SymbolStorageLocalFileImpl.java deleted file mode 100644 index ed4e0e63..00000000 --- a/java/common/src/com/google/airbag/common/SymbolStorageLocalFileImpl.java +++ /dev/null @@ -1,203 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * Implementation of SymbolStorage interface using a local file system. - * - * Given a set of parameters, prod, ver, app, plat, computes its MD5 digest. - * Digest + .attr contains attributes and checksum of the symbol file. - * Symbol file content is stored in a file named by its checksum + .data. - * - * To upload a symbol file, a client can send a query: - *
- *   /symexists?sum=, the server checks if a symbol file identified by checksum exists;
- *   /upload?prod=&ver=&plat=&app=&sum= use POST
- *   method with or without uploading a file.
- *   
- *   A client can always call /upload to upload a symbol file. 
- *   However, a more efficient way is to check whether a file is on the server by calling /symexists.
- *   If so, the client can just POST the request without actually upload the file content,
- *   checksum is sufficient.
- * 
- *   /getchecksum?prod=&ver=&plat=&app=, returns the checksum
- *   of the symbol file on the server.
- *   /download?prod=&ver=&plat=&app=, downloads the symbol file
- *   on the server.
- *   
- *   A client can always use /download to download a symbol file.
- *   However, if the client maintins its own cache of symbol files, it can call /getchecksum,
- *   and look up the cache using the checksum. If the cache does not have the file, then it 
- *   calls /download.
- * 
- */ - -public class SymbolStorageLocalFileImpl implements SymbolStorage { - - // Name extension of attribute file - private static final String ATTR_FILE_EXT = ".attr"; - - // Name extension of data file - private static final String DATA_FILE_EXT = ".data"; - - private static final int MAX_ATTR_FILE_LENGTH = 1 << 10; - - // Directory name for storing files. - private String directoryName; - - /** - * Creates an instance of ReportStorageLocalFileImpl by providing a - * directory name. - * - * @param dirname - * @throws IOException - */ - public SymbolStorageLocalFileImpl(String dirname) throws IOException { - this.directoryName = dirname; - // new File can throw NullPointerException if dirname is null - File dir = new File(dirname); - if (!dir.exists() && !dir.mkdirs()) - throw new IOException("Cannot make dirs for "+dirname); - - if (!dir.canWrite()) - throw new IOException("Cannot write to "+dirname - +", check your permissions."); - } - - /** - * Save a symbol file. - */ - public boolean saveSymbolFile(SortedMap attrs, InputStream contents) - throws IOException { - String digest = getAttributesSignature(attrs); - - // get 'sum' value - String checksum = attrs.get(NameConstants.CHECKSUM_PNAME); - if (checksum == null) - return false; - - // attribute file name and data file name - File attrFile = new File(this.directoryName, digest+ATTR_FILE_EXT); - - // use passed in checksum as file name - File dataFile = new File(this.directoryName, checksum+DATA_FILE_EXT); - - // write data to file - FileOutputStream outs = new FileOutputStream(dataFile); - CrashUtils.copyStream(contents, outs, 0); - outs.close(); - - // get signature of input stream - String filesig = CrashUtils.fileSignature(dataFile); - - if (!checksum.equals(filesig)) { - dataFile.delete(); - return false; - } - - // save all attributes with checksum - String fullAttrs = CrashUtils.attributesToString(attrs); - - FileOutputStream fos = new FileOutputStream(attrFile); - fos.write(fullAttrs.getBytes()); - fos.close(); - - return true; - } - - - public String getFileChecksum(SortedMap attrs) throws IOException { - String digest = getAttributesSignature(attrs); - File attrFile = new File(this.directoryName, digest+ATTR_FILE_EXT); - - if (!attrFile.isFile()) - throw new FileNotFoundException(); - - int length = (int) attrFile.length(); - if (length >= MAX_ATTR_FILE_LENGTH) - throw new FileNotFoundException(); - - byte[] content = new byte[length]; - - FileInputStream fis = new FileInputStream(attrFile); - fis.read(content); - fis.close(); - - // parse contents - SortedMap savedAttrs = - CrashUtils.stringToAttributes(new String(content)); - - return savedAttrs.get(NameConstants.CHECKSUM_PNAME); - } - - public boolean fileExists(String checksum) { - File dataFile = new File(this.directoryName, checksum+DATA_FILE_EXT); - return dataFile.isFile(); - } - - public InputStream openFileForRead(SortedMap attrs) - throws IOException { - String checksum = getFileChecksum(attrs); - if (checksum == null) - throw new FileNotFoundException(); - - File dataFile = new File(this.directoryName, checksum + DATA_FILE_EXT); - if (!dataFile.isFile()) - throw new FileNotFoundException(); - - return new FileInputStream(dataFile); - } - - private static final String[] requiredParameters = { - NameConstants.PRODUCT_PNAME, - NameConstants.APPLICATION_PNAME, - NameConstants.PLATFORM_PNAME, - NameConstants.VERSION_PNAME - }; - - private String getAttributesSignature(SortedMap attrs) { - // canonize parameters - SortedMap params = canonizeAttributes(attrs); - String attrString = CrashUtils.attributesToString(params); - return CrashUtils.dataSignature(attrString.getBytes()); - } - /* Canonize attributes, get 'prod', 'ver', 'plat', and 'app' values, - * and put them in a new sorted map. If one of value is missing, - * returns null. - */ - private SortedMap - canonizeAttributes(SortedMap attrs) { - SortedMap params = new TreeMap(); - for (String s : requiredParameters) { - String v = attrs.get(s); - if (v == null) - return null; - else - params.put(s, v); - } - return params; - } - -} diff --git a/java/common/src/com/google/airbag/common/TestReportStorageConsumer.java b/java/common/src/com/google/airbag/common/TestReportStorageConsumer.java deleted file mode 100644 index 6b40dee2..00000000 --- a/java/common/src/com/google/airbag/common/TestReportStorageConsumer.java +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.TreeMap; - -/** - * Test ReportStorage and its implementation by simulating two pocesses - * as producer and consuer. - * - * To use this test with TestReportStorageProducer: - * > java TestReportStorageProducer /tmp/testdir - * - * In another console, - * > java TestReportStorageConsumer /tmp/testdir - * - * Then watch output on both console. - */ -public class TestReportStorageConsumer { - - /** - * @param args - */ - public static void main(String[] args) { - String testdir = args[0]; - ReportStorage rs = null; - try { - rs = new ReportStorageLocalFileImpl(testdir); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - - consume(rs); - } - - private static void consume(ReportStorage rs) { - int i = 0; - TreeMap params = - new TreeMap(); - String v = "hello"; - byte[] buf = new byte[1024]; - while (true) { - params.put(Integer.toString(i), v); - String id = rs.getUniqueId(params); - rs.saveAttributes(id, params); - if (rs.reportExists(id)) { - InputStream is = null; - try { - is = rs.openReportForRead(id); - while (is.read(buf) != -1) { - System.out.print(new String(buf)); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - break; - } catch (IOException e) { - e.printStackTrace(); - break; - } - } - i++; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - break; - } - } - } -} diff --git a/java/common/src/com/google/airbag/common/TestReportStorageProducer.java b/java/common/src/com/google/airbag/common/TestReportStorageProducer.java deleted file mode 100644 index b52296b6..00000000 --- a/java/common/src/com/google/airbag/common/TestReportStorageProducer.java +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (C) 2006 Google Inc. - -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 - - http://www.apache.org/licenses/LICENSE-2.0 - -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.google.airbag.common; - -import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.TreeMap; - -/** - * Test ReportStorage and its implementation by simulating two pocesses - * as producer and consuer. - * - * To use this test with TestReportStorageConsumer: - * > java TestReportStorageProducer /tmp/testdir - * - * In another console, - * > java TestReportStorageConsumer /tmp/testdir - * - * Then watch output on both console. - */ - -public class TestReportStorageProducer { - - /** - * @param args - */ - public static void main(String[] args) { - String testdir = args[0]; - ReportStorage rs = null; - try { - rs = new ReportStorageLocalFileImpl(testdir); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - - produce(rs); - } - - private static void produce(ReportStorage rs) { - int i = 0; - TreeMap params = - new TreeMap(); - String v = "hello"; - ByteArrayInputStream ba = new ByteArrayInputStream("hello world!".getBytes()); - while (true) { - ba.reset(); - params.put(Integer.toString(i), v); - String id = rs.getUniqueId(params); - rs.saveAttributes(id, params); - if (id == null) { - System.exit(1); - } - try { - rs.writeStreamToReport(id, ba, 0); - } catch (FileNotFoundException e) { - e.printStackTrace(); - break; - } catch (IOException e) { - e.printStackTrace(); - break; - } - i++; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - break; - } - } - } -}