0001: package snow.watchdog;
0002:
0003: import snow.files.*;
0004: import java.util.prefs.Preferences;
0005: import java.net.*;
0006: import javax.net.ssl.*;
0007: import snow.utils.SysUtils;
0008: import snow.utils.DateUtils;
0009: import javax.swing.border.EmptyBorder;
0010: import java.awt.Insets;
0011: import java.awt.BorderLayout;
0012: import java.awt.SystemTray;
0013: import java.awt.event.*;
0014: import snow.utils.gui.*;
0015: import java.io.*;
0016: import java.awt.TrayIcon;
0017: import snow.utils.storage.*;
0018: import snow.sortabletable.*;
0019: import java.util.*;
0020: import javax.swing.*;
0021:
0022: /** Utility to observe some file / folders / urls for modifications.
0023: * VERY useful.
0024: */
0025: public final class WatchDog extends JFrame {
0026: private static WatchDog instance;
0027: // must be set before getting instance
0028: static boolean standalone = false;
0029:
0030: // data
0031: private final Vector<WatchedData> watchedDatas = new Vector<WatchedData>();
0032: private final static String[] watchObject = new String[] { "File",
0033: "HTML" }; // POP, FTP (login), already working, but not convivial, ...
0034: private final static String[] watchFreq = new String[] { "hour",
0035: "5 minutes", "day", "week", "month" };
0036: private final static String[] recursionDepthTypes = new String[] {
0037: "All", "None", "1", "2", "3", "4", "5", "6", "7", "8", "9",
0038: "10" };
0039:
0040: final private java.util.Timer updaterTimer = new java.util.Timer(
0041: true);
0042: static final File storageFile = new File(System
0043: .getProperty("user.home"), ".tide_global/watchdog.vec");
0044:
0045: private static TrayIcon trayIcon;
0046:
0047: final JTable table;
0048: final WatchedDataTable mod;
0049: final SortableTableModel stm;
0050:
0051: // NOT set to visible. Is silent if no watch active.
0052: private WatchDog(boolean standalone) {
0053: super ("tIDE's eyes");
0054: this .setIconImage(Icons.createImage(new Icons.EyeIcon(16, 16,
0055: true)));
0056:
0057: instance = this ;
0058:
0059: setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
0060:
0061: load();
0062:
0063: mod = new WatchedDataTable();
0064: stm = new SortableTableModel(mod);
0065: table = new JTable(stm);
0066: stm.installGUI(table);
0067: add(new JScrollPane(table), BorderLayout.CENTER);
0068:
0069: table.addMouseListener(new MouseAdapter() {
0070: @Override
0071: public void mousePressed(MouseEvent me) {
0072: }
0073:
0074: @Override
0075: public void mouseReleased(MouseEvent me) {
0076: }
0077:
0078: @Override
0079: public void mouseClicked(MouseEvent me) {
0080: if (me.getClickCount() == 2) {
0081: int[] sel = table.getSelectedRows();
0082: if (sel.length != 1)
0083: return;
0084: int ed = stm.getIndexInUnsortedFromTablePos(sel[0]);
0085: WatchedData cd = watchedDatas.get(ed);
0086: WatcherEditor cde = new WatcherEditor(cd);
0087: mod.update();
0088: save();
0089: }
0090: }
0091:
0092: });
0093:
0094: JPanel cp = new JPanel();
0095: cp.setBorder(new EmptyBorder(2, 2, 2, 2));
0096: cp.setLayout(new BoxLayout(cp, BoxLayout.X_AXIS));
0097:
0098: add(cp, BorderLayout.NORTH);
0099:
0100: JButton add = new JButton("add new eye", Icons.sharedPlus);
0101: add.setMargin(new Insets(1, 1, 1, 1));
0102: cp.add(add);
0103: add.addActionListener(new ActionListener() {
0104: public void actionPerformed(ActionEvent ae) {
0105: WatchedData cd = new WatchedData();
0106: watchedDatas.add(cd);
0107: mod.update();
0108:
0109: WatcherEditor cde = new WatcherEditor(cd);
0110: mod.update();
0111: save();
0112: }
0113: });
0114:
0115: JButton remove = new JButton("remove selected",
0116: Icons.sharedCross);
0117: remove.setMargin(new Insets(1, 1, 1, 1));
0118: cp.add(Box.createHorizontalStrut(5));
0119: cp.add(remove);
0120: remove.addActionListener(new ActionListener() {
0121: public void actionPerformed(ActionEvent ae) {
0122: int[] sel = table.getSelectedRows();
0123: //if(sel.length!=1) return;
0124: List<WatchedData> rem = new ArrayList<WatchedData>();
0125: for (int si : sel) {
0126: int ed = stm.getIndexInUnsortedFromTablePos(si);
0127: WatchedData cd = watchedDatas.get(ed);
0128: rem.add(cd);
0129: }
0130: watchedDatas.removeAll(rem);
0131:
0132: mod.update();
0133: save();
0134: }
0135: });
0136:
0137: JButton looknow = new JButton("look now", new Icons.EyeIcon(16,
0138: 16, true));
0139: looknow.setMargin(new Insets(1, 1, 1, 1));
0140: cp.add(Box.createHorizontalStrut(25));
0141: cp.add(looknow);
0142: looknow.addActionListener(new ActionListener() {
0143: public void actionPerformed(ActionEvent ae) {
0144: int[] sel = table.getSelectedRows();
0145: for (int si : sel) {
0146: int ed = stm.getIndexInUnsortedFromTablePos(si);
0147: WatchedData cd = watchedDatas.get(ed);
0148: cd.forceLookNow = true;
0149: }
0150:
0151: Thread t = new Thread() {
0152: public void run() {
0153: watch();
0154: }
0155: };
0156: t.start();
0157:
0158: }
0159: });
0160:
0161: cp.add(Box.createHorizontalGlue());
0162: if (standalone) {
0163: JButton close = new JButton("quit", Icons.sharedCross);
0164: close.setMargin(new Insets(1, 1, 1, 1));
0165: cp.add(close);
0166: close.addActionListener(new ActionListener() {
0167: public void actionPerformed(ActionEvent ae) {
0168: System.exit(0);
0169: }
0170: });
0171: } else {
0172: // options within tide.
0173:
0174: boolean auto = Preferences.userRoot().getBoolean(
0175: "tide_eye_autostart", true);
0176: final JCheckBox cbAutoStart = new JCheckBox("Autostart",
0177: auto);
0178: cp.add(cbAutoStart);
0179: cbAutoStart.addActionListener(new ActionListener() {
0180: public void actionPerformed(ActionEvent ae) {
0181: Preferences.userRoot().putBoolean(
0182: "tide_eye_autostart",
0183: cbAutoStart.isSelected());
0184: }
0185: });
0186:
0187: JButton stop = new JButton("stop", Icons.sharedStop);
0188: stop.setMargin(new Insets(1, 1, 1, 1));
0189: cp.add(stop);
0190: stop.addActionListener(new ActionListener() {
0191: public void actionPerformed(ActionEvent ae) {
0192: try {
0193: instance = null;
0194: if (trayIcon != null) {
0195: SystemTray.getSystemTray().remove(trayIcon);
0196: updaterTimer.cancel();
0197: trayIcon = null;
0198: }
0199: } catch (Exception e) {
0200: e.printStackTrace();
0201: }
0202: }
0203: });
0204: }
0205:
0206: MultiSearchPanel searchPanel = new MultiSearchPanel("Filter: ",
0207: null, stm);
0208: add(searchPanel, BorderLayout.SOUTH);
0209:
0210: setSize(800, 400);
0211: this .setLocationRelativeTo(null);
0212:
0213: updaterTimer.schedule(new TimerTask() {
0214: public final void run() {
0215: //cdt.update(); // no, a repaint is enough
0216: if (isVisible()) {
0217: table.repaint();
0218: }
0219:
0220: //EventQueue.invokeLater(new Runnable() { public void run() {
0221: watch();
0222: //}});
0223: }
0224:
0225: }, 2000L, 10000L);
0226:
0227: installTrayIfNecessary();
0228: }
0229:
0230: /** Called from time to time (10 sec). */
0231: void watch() {
0232: WatchedData[] datas = watchedDatas
0233: .toArray(new WatchedData[watchedDatas.size()]);
0234: boolean hasNewLooks = false;
0235:
0236: for (final WatchedData ci : datas) // avoid concurrent mod ex...
0237: {
0238: if (!ci.isLookNecessary())
0239: continue;
0240:
0241: ProgressModalDialog pmd = new ProgressModalDialog(
0242: new JFrame(), "" + ci.name, false);
0243: final String status = ci.shouldAlertNow(pmd);
0244: pmd.closeDialog();
0245:
0246: if (status != null) {
0247:
0248: if (trayIcon != null) {
0249: trayIcon.displayMessage("" + ci.name, status,
0250: TrayIcon.MessageType.INFO);
0251: }
0252:
0253: toFront();
0254: setVisible(true);
0255:
0256: if (ci.objectKind == 1) {
0257: int rep = JOptionPane.showConfirmDialog(this ,
0258: status + "\n\nOpen URL in browser ?", ""
0259: + ci.name,
0260: JOptionPane.YES_NO_CANCEL_OPTION);
0261: if (rep == JOptionPane.OK_OPTION) {
0262: try {
0263: SysUtils.openBrowser(ci.target);
0264: } catch (Exception e) {
0265: e.printStackTrace();
0266: }
0267: }
0268: } else {
0269: showChanges(ci, status);
0270:
0271: }
0272:
0273: }
0274: hasNewLooks = true;
0275: }
0276:
0277: if (hasNewLooks) {
0278: save();
0279: }
0280: }
0281:
0282: private void showChanges(WatchedData ci, String details) {
0283: JPanel p = new JPanel(new BorderLayout());
0284:
0285: JComponent ta = GUIUtils.createReadOnlyDescriptionArea(details
0286: .trim());
0287: p.add(new JScrollPane(ta), BorderLayout.NORTH);
0288:
0289: JTabbedPane tp = new JTabbedPane();
0290: p.add(tp, BorderLayout.CENTER);
0291:
0292: if (!ci.foundChangedFiles.isEmpty()) {
0293: tp.add(ci.foundChangedFiles.size() + " changed files",
0294: createSearchableTable(ci.foundChangedFiles));
0295: }
0296:
0297: if (!ci.foundNewFiles.isEmpty()) {
0298: tp.add(ci.foundNewFiles.size() + " new files",
0299: createSearchableTable(ci.foundNewFiles));
0300: }
0301:
0302: if (!ci.foundNewFolders.isEmpty()) {
0303: tp.add(ci.foundNewFolders.size() + " new folders",
0304: createSearchableTable(ci.foundNewFolders));
0305: }
0306:
0307: if (!ci.foundRemovedFiles.isEmpty()) {
0308: tp.add(ci.foundRemovedFiles.size() + " removed files",
0309: createSearchableTable(ci.foundRemovedFiles));
0310: }
0311:
0312: if (!ci.foundRemovedFolders.isEmpty()) {
0313: tp.add(ci.foundRemovedFolders.size() + " removed folders",
0314: createSearchableTable(ci.foundRemovedFolders));
0315: }
0316:
0317: GUIUtils.displayInDialog(this , "" + ci.name + ": "
0318: + ci.totChanges + " changes", p);
0319:
0320: }
0321:
0322: private JPanel createSearchableTable(List<JFile> files) {
0323: final JFilesTableModel htm = new JFilesTableModel(files);
0324: final SortableTableModel stm = new SortableTableModel(htm);
0325: final JTable table = new JTable(stm);
0326: stm.installGUI(table);
0327: return SortableTableUtils.createTableWithSearchBar(stm, table);
0328:
0329: }
0330:
0331: public static synchronized WatchDog getInstance() {
0332: if (instance != null)
0333: return instance;
0334: instance = new WatchDog(standalone);
0335: return instance;
0336: }
0337:
0338: /** Only if active watchers are present.
0339: * Should be called at each changes.
0340: */
0341: public static void installTrayIfNecessary() {
0342: if (!lookIfActiveWatchersArePresent())
0343: return;
0344: if (!SystemTray.isSupported())
0345: return;
0346: if (trayIcon != null)
0347: return;
0348:
0349: int w = (int) SystemTray.getSystemTray().getTrayIconSize()
0350: .getWidth();
0351: trayIcon = new TrayIcon(Icons.createImage(new Icons.EyeIcon(w,
0352: w, true)), "tIDE's eye");
0353: try {
0354: SystemTray.getSystemTray().add(trayIcon);
0355: } catch (Exception e) {
0356: e.printStackTrace();
0357: }
0358:
0359: trayIcon.addMouseListener(new MouseAdapter() {
0360: @Override
0361: public void mousePressed(MouseEvent me) {
0362: }
0363:
0364: @Override
0365: public void mouseReleased(MouseEvent me) {
0366: }
0367:
0368: @Override
0369: public void mouseClicked(MouseEvent me) {
0370: getInstance().setVisible(true);
0371: }
0372: });
0373:
0374: // initialize the counter
0375: getInstance();
0376: }
0377:
0378: /** Helper...
0379: */
0380: public static boolean lookIfActiveWatchersArePresent() {
0381: try {
0382: if (!storageFile.exists())
0383: return false;
0384:
0385: List<WatchedData> datas = loadData();
0386: if (datas.size() > 0)
0387: return true; // OK, we have at least one !
0388:
0389: return false;
0390: } catch (Exception ex) {
0391: JOptionPane.showMessageDialog(null,
0392: "Can't read the stored settings:\n\n "
0393: + ex.getMessage(), "WatchDog Error",
0394: JOptionPane.ERROR_MESSAGE);
0395: ex.printStackTrace();
0396: return false;
0397: }
0398: }
0399:
0400: @SuppressWarnings("unchecked")
0401: private static List<WatchedData> loadData() throws Exception {
0402: List<WatchedData> datas = new ArrayList<WatchedData>();
0403: if (storageFile.exists()) {
0404: List<Object> reps = FileUtils
0405: .loadVectorFromFile(storageFile);
0406:
0407: if (reps.get(0) instanceof Integer) // version 2+
0408: {
0409: int version = (Integer) reps.get(0);
0410: List<Object> repsi = null;
0411: if (version == 1) {
0412: repsi = (List<Object>) reps.get(1);
0413: } else {
0414: //autostart = (Boolean) reps.get(1);
0415: repsi = (List<Object>) reps.get(2);
0416: }
0417:
0418: for (Object ri : repsi) {
0419: WatchedData cd = new WatchedData();
0420: cd.setFromRep((List<Object>) ri);
0421: datas.add(cd);
0422: }
0423: } else {
0424: for (Object ri : reps) {
0425: WatchedData cd = new WatchedData();
0426: cd.setFromRep((List<Object>) ri);
0427: datas.add(cd);
0428: }
0429: }
0430: }
0431: return datas;
0432: }
0433:
0434: @SuppressWarnings("unchecked")
0435: private void load() {
0436: try {
0437: watchedDatas.clear();
0438: watchedDatas.addAll(loadData());
0439: System.out.println("Loaded " + watchedDatas.size()
0440: + " tide eyes");
0441: } catch (Exception ex) {
0442: JOptionPane.showMessageDialog(null,
0443: "Can't read the stored settings:\n\n "
0444: + ex.getMessage(), "WatchDog Error",
0445: JOptionPane.ERROR_MESSAGE);
0446: ex.printStackTrace();
0447: }
0448: }
0449:
0450: /** Should be called on each changes.
0451: */
0452: @SuppressWarnings("unchecked")
0453: private void save() {
0454: System.out.println("saving " + watchedDatas.size()
0455: + " tide's eyes...");
0456:
0457: if (!storageFile.getParentFile().exists())
0458: storageFile.getParentFile().mkdirs();
0459: Vector rep = new Vector();
0460: rep.add(2);
0461: rep.add(false); // old autostart
0462: Vector repi = new Vector();
0463: rep.add(repi);
0464: for (final WatchedData ci : watchedDatas) {
0465: repi.add(ci.getRep());
0466: }
0467:
0468: try {
0469: synchronized (this ) {
0470: File tempDest = File.createTempFile("tide_watchdog",
0471: ".vec");
0472: tempDest.deleteOnExit();
0473: FileUtils.saveVectorToFile(tempDest,
0474: (Vector<Object>) rep);
0475: if (storageFile.exists()) {
0476: if (!storageFile.delete()) {
0477: System.out.println("Can't delete old eye");
0478: }
0479: }
0480: if (!tempDest.renameTo(storageFile)) {
0481: System.out.println("Can't rename " + tempDest
0482: + " tide's eye temp to " + storageFile);
0483: System.out.println("temp: " + tempDest.exists()
0484: + ": " + tempDest.length());
0485: System.out.println("dest: " + storageFile.exists()
0486: + ": " + storageFile.length()
0487: + "... try copy...");
0488: FileUtils.copy(tempDest, storageFile);
0489: }
0490: }
0491: } catch (Exception ex) {
0492: JOptionPane.showMessageDialog(null, "Error", ""
0493: + ex.getMessage(), JOptionPane.ERROR_MESSAGE);
0494: ex.printStackTrace();
0495: }
0496:
0497: installTrayIfNecessary();
0498:
0499: System.out.println("save done...");
0500: }
0501:
0502: class WatchedDataTable extends FineGrainTableModel {
0503: int[] COLUMN_PREFERED_SIZES = new int[] { 10, 9, 4, 18, 10 };
0504: final private String[] COLUMN_NAMES = new String[] { "Name",
0505: "Kind", "Freq", "Target", "Last status" };
0506:
0507: public WatchedDataTable() {
0508: }
0509:
0510: public Object getValueAt(int row, int column) {
0511: final WatchedData ci = watchedDatas.get(row);
0512: if (column == 0)
0513: return ci.name;
0514: if (column == 1) {
0515: if (ci.objectKind == 0)
0516: return "Files (recursion: "
0517: + recursionDepthTypes[ci.recursionDepth]
0518: + ")";
0519: else if (ci.objectKind == 1)
0520: return "Http";
0521: else
0522: return "FTP";
0523: }
0524: if (column == 2)
0525: return "each " + watchFreq[ci.freq];
0526: if (column == 3)
0527: return ci.target;
0528: if (column == 4) {
0529: if (ci.lastWatch > 0) {
0530: return DateUtils
0531: .formatDateAndTimeHuman(ci.lastWatch)
0532: + ": " + ci.lastStatus;
0533: } else {
0534: return "";
0535: }
0536: }
0537: //if(column==3) return ci;
0538: return "";
0539: }
0540:
0541: public int getRowCount() {
0542: return watchedDatas.size();
0543: }
0544:
0545: public void update() {
0546: this .fireTableModelWillChange();
0547: this .fireTableDataChanged();
0548: this .fireTableModelHasChanged();
0549: }
0550:
0551: @Override
0552: public int getPreferredColumnWidth(int column) {
0553: if (column >= 0 && column < COLUMN_PREFERED_SIZES.length)
0554: return COLUMN_PREFERED_SIZES[column];
0555: return -1;
0556: }
0557:
0558: @Override
0559: public String getColumnName(int column) {
0560: if (column >= 0 && column < COLUMN_NAMES.length)
0561: return COLUMN_NAMES[column];
0562: return "Bad column " + column;
0563: }
0564:
0565: public int getColumnCount() {
0566: return COLUMN_NAMES.length;
0567: }
0568: }
0569:
0570: static class WatchedData implements Comparable<WatchedData> {
0571: public final int compareTo(final WatchedData o) {
0572: return name.compareTo(o.name);
0573: }
0574:
0575: long created = System.currentTimeMillis();
0576:
0577: int objectKind = 0; // File, Http, Ftp
0578: String target = "";
0579: String name = "?";
0580: final int fileKind = 0;
0581:
0582: int freq = 0, recursionDepth = 1; // 0=All, 1=none, 2=1, ...
0583:
0584: // for alert
0585: long lastWatch = -1;
0586: boolean lastExisting = true; // first comp key !
0587: private final List<SF> lastFolders = new ArrayList<SF>();
0588: private final List<SF> lastFiles = new ArrayList<SF>();
0589:
0590: String lastStatus = "not analysed yet";
0591:
0592: private boolean forceLookNow = false;
0593:
0594: public void reset() {
0595: lastWatch = -1;
0596: lastExisting = true;
0597: lastFolders.clear();
0598: lastFiles.clear();
0599: }
0600:
0601: // depends on kind value
0602: public void collectFilesAndFolders(final List<SF> files,
0603: final List<SF> folders, final ProgressModalDialog pmd) {
0604: File pa = getPath();
0605: if (pa == null) {
0606: return;
0607: }
0608: /*String pat = pa.getAbsolutePath();
0609: int baseL = pat.length();
0610: if(!pat.endsWith("/") && !pat.endsWith("\\")) baseL++;*/
0611:
0612: final int baseL = FileUtils.getCanonicalName(pa).length();
0613:
0614: if (fileKind == 0) // all files and folders recurse
0615: {
0616: final List<File> allFilesAndDirs = new ArrayList<File>();
0617: if (recursionDepth == 0) // all
0618: {
0619: FileUtils.getAllFilesRecurseLim(pa,
0620: allFilesAndDirs, true, false, 1024, pmd);
0621: } else {
0622: FileUtils.getAllFilesRecurseLim(pa,
0623: allFilesAndDirs, true, false,
0624: recursionDepth - 1, pmd);
0625: }
0626:
0627: for (File fi : allFilesAndDirs) {
0628: if (fi.isDirectory())
0629: folders.add(new SF(fi, baseL));
0630: else
0631: files.add(new SF(fi, baseL));
0632: }
0633: } else if (fileKind == 1) // new level0 folders
0634: {
0635: File[] folds = pa
0636: .listFiles(FileUtils.folderFileNameFilter);
0637: if (folds != null)
0638: for (File fi : folds)
0639: folders.add(new SF(fi, baseL));
0640: } else if (fileKind == 2) // new level0 files (and folders)
0641: {
0642: File[] fis = pa.listFiles();
0643: if (fis != null)
0644: for (File fi : fis)
0645: files.add(new SF(fi, baseL));
0646: }
0647: }
0648:
0649: public StorageVector getRep() {
0650: StorageVector rep = new StorageVector();
0651: rep.add(1);
0652: rep.add(objectKind);
0653: rep.add(name);
0654: rep.add(created);
0655: rep.add(target);
0656: rep.add(fileKind);
0657: rep.add(freq);
0658:
0659: rep.add(lastWatch); //7
0660: rep.add(lastExisting);
0661:
0662: List<Object> repf = new ArrayList<Object>();
0663: rep.add(repf); // 9
0664: for (SF fi : lastFolders) {
0665: repf.add(fi.getRep());
0666: }
0667:
0668: List<Object> repfi = new ArrayList<Object>();
0669: rep.add(repfi); // 10
0670: for (SF fi : lastFiles) {
0671: repfi.add(fi.getRep());
0672: }
0673:
0674: rep.add(recursionDepth);
0675: rep.add(lastStatus);
0676:
0677: return rep;
0678: }
0679:
0680: @SuppressWarnings("unchecked")
0681: public void setFromRep(List<Object> rep) {
0682: objectKind = (Integer) rep.get(1);
0683: name = (String) rep.get(2);
0684: created = (Long) rep.get(3);
0685: target = (String) rep.get(4);
0686: //fileKind = (Integer) rep.get(5);
0687: freq = (Integer) rep.get(6);
0688:
0689: lastWatch = (Long) rep.get(7);
0690: lastExisting = (Boolean) rep.get(8);
0691:
0692: List<Object> repf = (List<Object>) rep.get(9);
0693: lastFolders.clear();
0694: for (Object ri : repf) {
0695: lastFolders.add(new SF((List<Object>) ri));
0696: }
0697:
0698: List<Object> repfi = (List<Object>) rep.get(10);
0699: lastFiles.clear();
0700: for (Object ri : repfi) {
0701: lastFiles.add(new SF((List<Object>) ri));
0702: }
0703:
0704: if (rep.size() > 11) {
0705: recursionDepth = (Integer) rep.get(11);
0706: }
0707: if (rep.size() > 12) {
0708: lastStatus = (String) rep.get(12);
0709: }
0710: }
0711:
0712: public boolean isLookNecessary() {
0713: if (target.trim().length() == 0) {
0714: lastStatus = "no target.";
0715: return false;
0716: }
0717:
0718: if (forceLookNow)
0719: return true;
0720:
0721: if (System.currentTimeMillis() - lastWatch > getMillisForWatchFrequency(freq))
0722: return true;
0723:
0724: return false;
0725: }
0726:
0727: // todo: return this in some struct
0728: public List<JFile> foundNewFiles = new ArrayList<JFile>();
0729: public List<JFile> foundNewFolders = new ArrayList<JFile>();
0730:
0731: public List<JFile> foundRemovedFiles = new ArrayList<JFile>();
0732: public List<JFile> foundRemovedFolders = new ArrayList<JFile>();
0733:
0734: public List<JFile> foundChangedFiles = new ArrayList<JFile>();
0735:
0736: int totChanges = 0;
0737:
0738: /** @return non null => alert.
0739: */
0740: public String shouldAlertNow(final ProgressModalDialog pmd) {
0741: if (target.trim().length() == 0) {
0742: lastStatus = "no target.";
0743: return null;
0744: }
0745:
0746: if (!forceLookNow) {
0747: if (System.currentTimeMillis() - lastWatch < getMillisForWatchFrequency(freq)) {
0748: return null;
0749: }
0750: }
0751: forceLookNow = false;
0752:
0753: foundNewFiles.clear();
0754: foundNewFolders.clear();
0755: foundRemovedFiles.clear();
0756: foundRemovedFolders.clear();
0757: foundChangedFiles.clear();
0758:
0759: long cachedLastWatch = lastWatch;
0760: lastStatus = "analysing...";
0761:
0762: lastWatch = System.currentTimeMillis();
0763:
0764: if (objectKind == 0) // FILE
0765: {
0766: File tf = getPath();
0767: System.out.println("Look in " + tf + ", rec="
0768: + recursionDepthTypes[recursionDepth]);
0769:
0770: if (tf.exists() != lastExisting) {
0771: lastExisting = tf.exists();
0772: if (tf.exists())
0773: return "" + target + " appeared";
0774: lastStatus = "path not existing";
0775: return "" + target + " path not existing";
0776: }
0777:
0778: final List<SF> actFolders = new ArrayList<SF>();
0779: final List<SF> actFiles = new ArrayList<SF>();
0780:
0781: if (pmd != null)
0782: pmd.startDelayed(1000L);
0783: collectFilesAndFolders(actFiles, actFolders, pmd); // all act: visit = false
0784: if (pmd != null)
0785: pmd.closeDialog();
0786:
0787: // create quick lookups and mark as not visited
0788: Map<String, SF> qLastFiles = new HashMap<String, SF>();
0789: for (SF fi : lastFiles) {
0790: qLastFiles.put(fi.relName, fi);
0791: fi.visited = false;
0792: }
0793:
0794: Map<String, SF> qLastFolders = new HashMap<String, SF>();
0795: for (SF fi : lastFolders) {
0796: qLastFolders.put(fi.relName, fi);
0797: fi.visited = false;
0798: }
0799:
0800: // Compare
0801: final StringBuilder comparationResult = new StringBuilder();
0802:
0803: // Folders analysis
0804: int newFolders = 0;
0805: for (SF fi : actFolders) {
0806: if (!qLastFolders.containsKey(fi.relName)) {
0807: //comparationResult.append("\nnew folder: "+fi.relName);
0808: foundNewFolders
0809: .add(new JFile(fi.f, fi.relName));
0810: newFolders++;
0811: } else {
0812: qLastFolders.get(fi.relName).visited = true;
0813: }
0814: }
0815:
0816: // disapeared
0817: int removedFolders = 0;
0818: for (SF fi : lastFolders) {
0819: if (!fi.visited) {
0820: //comparationResult.append("\nfolder removed: "+fi.relName);
0821: foundRemovedFolders.add(new JFile(fi.relName));
0822: removedFolders++;
0823: }
0824: }
0825:
0826: // Files analysis
0827: int newFiles = 0;
0828: int removedFiles = 0;
0829: int changedFiles = 0;
0830:
0831: List<SF> newAppearedFiles = new ArrayList<SF>();
0832: for (SF fi : actFiles) {
0833: if (!qLastFiles.containsKey(fi.relName)) {
0834: //comparationResult.append("\nnew file: "+fi.relName);
0835: newAppearedFiles.add(fi);
0836: newFiles++;
0837: } else {
0838: SF lfi = qLastFiles.get(fi.relName);
0839: lfi.visited = true;
0840:
0841: // look for changes
0842: if (lfi.lastMod != fi.lastMod) {
0843: //comparationResult.append("\nfile changed: "+fi.relName);
0844: foundChangedFiles.add(new JFile(fi.f,
0845: fi.relName));
0846: changedFiles++;
0847: }
0848: }
0849: }
0850:
0851: for (SF fi : newAppearedFiles) {
0852: //comparationResult.append("\nnew file: "+fi.relName);
0853: foundNewFiles.add(new JFile(fi.f, fi.relName));
0854: }
0855:
0856: // disapeared
0857: for (SF fi : lastFiles) {
0858: if (!fi.visited) {
0859: //comparationResult.append("\nfile removed: "+fi.relName);
0860: foundRemovedFiles.add(new JFile(fi.relName));
0861: removedFiles++;
0862: }
0863: }
0864:
0865: totChanges = newFolders + removedFolders + newFiles
0866: + removedFiles + changedFiles;
0867:
0868: if (totChanges > 0) //comparationResult.length()>0)
0869: {
0870: // set as the new ones !
0871: lastFiles.clear();
0872: lastFiles.addAll(actFiles);
0873: lastFolders.clear();
0874: lastFolders.addAll(actFolders);
0875:
0876: String head = "Root analysed: " + tf;
0877: head += "\n Analysis made at "
0878: + DateUtils.dateFormatMDYhm
0879: .format(lastWatch);
0880: if (cachedLastWatch > 0) {
0881: head += "\n Previous watch: "
0882: + DateUtils.dateFormatMDYhm
0883: .format(cachedLastWatch);
0884: }
0885: head += "\n Recursion: "
0886: + recursionDepthTypes[recursionDepth]
0887: + "\n Present: " + actFiles.size()
0888: + " files, " + actFolders.size()
0889: + " folders\n\nSummary:";
0890:
0891: if (changedFiles > 0)
0892: head += "\n " + changedFiles
0893: + " changed file"
0894: + (changedFiles == 1 ? "" : "s");
0895: if (newFolders > 0)
0896: head += "\n " + newFolders + " new folder"
0897: + (newFolders == 1 ? "" : "s");
0898: if (newFiles > 0)
0899: head += "\n " + newFiles + " new file"
0900: + (newFiles == 1 ? "" : "s");
0901: if (removedFiles > 0)
0902: head += "\n " + removedFiles
0903: + " removed file"
0904: + (removedFiles == 1 ? "" : "s");
0905: if (removedFolders > 0)
0906: head += "\n " + removedFolders
0907: + " removed folder"
0908: + (removedFolders == 1 ? "" : "s");
0909:
0910: //head += "\n\n\nDetails:";
0911:
0912: lastStatus = "" + totChanges + " change"
0913: + (totChanges == 0 ? "" : "s");
0914:
0915: return head.trim() + "\n"
0916: + comparationResult.toString().trim();
0917: } else {
0918: lastStatus = "no changes";
0919: }
0920: } else if (objectKind == 1) // HTTP
0921: {
0922: lastFolders.clear(); // not used.
0923:
0924: if (target.trim().length() == 0) {
0925: lastStatus = "no URL";
0926: return null;
0927: }
0928:
0929: try {
0930: //long[] das = NetUtils.getDateAndSizeOnServer(new URL(target.trim()));
0931: System.out.println("LOOK " + target);
0932:
0933: long hash = computeHashForURLContent(new URL(target
0934: .trim()));
0935:
0936: SF newSF = new SF(target.trim(), hash);
0937:
0938: if (lastFiles.isEmpty()) {
0939: lastFiles.add(newSF);
0940: lastStatus = "appeared";
0941: return "new: " + target;
0942: }
0943: SF old = lastFiles.get(0);
0944:
0945: if (old.lastMod != hash) {
0946: lastFiles.clear();
0947: lastFiles.add(newSF);
0948: lastStatus = "changed";
0949: return "changed: " + target;
0950: }
0951:
0952: lastFiles.clear();
0953: lastFiles.add(newSF);
0954: } catch (Exception e) {
0955: e.printStackTrace();
0956: lastStatus = "Error: " + e.getMessage();
0957: return "Error: " + e.getMessage();
0958: }
0959: } else {
0960: lastStatus = "ERROR, unknown watch type " + objectKind;
0961: return "ERROR, unknown watch type " + objectKind;
0962: }
0963:
0964: lastStatus = "no changes";
0965: return null;
0966: }
0967:
0968: public File getPath() {
0969: if (target.trim().length() == 0)
0970: return null;
0971: return new File(target.trim());
0972: }
0973:
0974: }
0975:
0976: private static long getMillisForWatchFrequency(int wf) {
0977: if (wf == 0)
0978: return 1000L * 3600; // hour
0979: if (wf == 1)
0980: return 1000L * 60 * 5; // 5 min
0981: if (wf == 2)
0982: return 1000L * 3600 * 24; // day
0983: if (wf == 3)
0984: return 1000L * 3600 * 24 * 7; // week
0985: if (wf == 4)
0986: return 1000L * 3600 * 24 * 30; // month
0987:
0988: throw new RuntimeException("bad freq " + wf);
0989: }
0990:
0991: /** Stored file or folder */
0992: static class SF {
0993: String relName;
0994: long lastMod;
0995: boolean visited = false;
0996:
0997: @Override
0998: public int hashCode() {
0999: return relName.hashCode();
1000: }
1001:
1002: @Override
1003: public boolean equals(Object o) {
1004: return relName.equals(((SF) o).relName);
1005: }
1006:
1007: // for a file
1008: File f;
1009:
1010: public SF(File f, int baseL) {
1011: this .f = f;
1012: this .relName = f.getAbsolutePath().substring(baseL);
1013: this .lastMod = f.lastModified();
1014: }
1015:
1016: public SF(String name, long lastMod) {
1017: this .relName = name;
1018: this .lastMod = lastMod;
1019: }
1020:
1021: // for a stored rep
1022: public SF(List<Object> rep) {
1023: this .relName = (String) rep.get(1);
1024: this .lastMod = (Long) rep.get(2);
1025: }
1026:
1027: public List<Object> getRep() {
1028: List<Object> rep = new ArrayList<Object>();
1029: rep.add(1);
1030: rep.add(relName);
1031: rep.add(lastMod);
1032:
1033: return rep;
1034: }
1035: }
1036:
1037: class WatcherEditor extends JDialog {
1038: private final JTextField nameTF = new JTextField("", 12);
1039: private final JTextField targetURLTF = new JTextField(
1040: "http://", 14);
1041: private final FileField target = new FileField("", false,
1042: "Please choose a file/folder to watch",
1043: JFileChooser.FILES_AND_DIRECTORIES);
1044:
1045: //private final JTextArea detailsTF = new JTextArea("", 2, 30);
1046:
1047: private JComboBox objCB = new JComboBox(watchObject); // File / HTTP
1048: private JComboBox freqCB = new JComboBox(watchFreq);
1049: //private JComboBox kindCB = new JComboBox(watchKind);
1050: private JComboBox recursionCB = new JComboBox(
1051: recursionDepthTypes);
1052:
1053: final private java.util.Timer remTimer = new java.util.Timer(
1054: true);
1055:
1056: final private List<JComponent> forURL = new ArrayList<JComponent>();
1057: final private List<JComponent> forFile = new ArrayList<JComponent>();
1058:
1059: public WatcherEditor(final WatchedData cd) {
1060: super (WatchDog.this , "Eye editor", true);
1061: setData(cd);
1062:
1063: JPanel cp = new JPanel();
1064: add(cp, BorderLayout.CENTER);
1065: GridLayout3 gl3 = new GridLayout3(2, cp);
1066:
1067: gl3.add(objCB);
1068: gl3.add("");
1069: objCB.addActionListener(new ActionListener() {
1070: public void actionPerformed(ActionEvent ae) {
1071: setFileVisible(objCB.getSelectedIndex() == 0);
1072: }
1073: });
1074:
1075: gl3.add("Name");
1076: gl3.add(nameTF);
1077:
1078: forURL.add(gl3.add("Target URL"));
1079: forURL.add(gl3.add(targetURLTF, true));
1080:
1081: forFile.add(gl3.add("Target"));
1082: forFile.add(gl3.add(target, true));
1083: /*
1084: forFile.add(gl3.add("Look at"));
1085: forFile.add(gl3.add(kindCB));
1086: */
1087:
1088: forFile.add(gl3.add("Recursion depth"));
1089: forFile.add(gl3.add(recursionCB));
1090: recursionCB.setMaximumRowCount(20);
1091:
1092: gl3.add("Frequency");
1093: gl3.add(freqCB);
1094:
1095: final CloseControlPanel ccp = new CloseControlPanel(this ,
1096: true, true, "Ok");
1097: add(ccp, BorderLayout.SOUTH);
1098:
1099: pack(); // let place for all.
1100:
1101: setFileVisible(objCB.getSelectedIndex() == 0);
1102:
1103: this .setLocationRelativeTo(null);
1104: setVisible(true); // MODAL
1105:
1106: remTimer.cancel();
1107:
1108: if (ccp.getWasCancelled())
1109: return; // do nothing
1110:
1111: cd.reset(); // REINITIALIZE !
1112: getData(cd);
1113:
1114: // first scan !
1115: // analyse
1116: Thread ant = new Thread() {
1117: public void run() {
1118:
1119: ProgressModalDialog pmd = new ProgressModalDialog(
1120: WatchDog.this , "" + cd.name, false);
1121: String det = cd.shouldAlertNow(pmd);
1122:
1123: if (pmd.getWasCancelled()) {
1124: JOptionPane.showMessageDialog(WatchDog.this ,
1125: "Watch cancelled", "" + cd.name,
1126: JOptionPane.INFORMATION_MESSAGE);
1127: return;
1128: }
1129:
1130: save();
1131:
1132: if (det != null) {
1133: String sum = det;
1134: if (sum.length() > 300) {
1135: sum = det.substring(0, det
1136: .indexOf("\n\n\n"));
1137: }
1138:
1139: JOptionPane.showMessageDialog(WatchDog.this , ""
1140: + sum, "" + cd.name,
1141: JOptionPane.INFORMATION_MESSAGE);
1142: }
1143: }
1144: };
1145: ant.start();
1146:
1147: }
1148:
1149: public void setFileVisible(boolean is) {
1150: for (JComponent ci : forURL) {
1151: ci.setVisible(!is);
1152: }
1153:
1154: for (JComponent ci : forFile) {
1155: ci.setVisible(is);
1156: }
1157:
1158: if (!is) {
1159: if (targetURLTF.getText().length() == 0) {
1160: targetURLTF.setText("http://");
1161: }
1162: }
1163: }
1164:
1165: private void setData(final WatchedData cd) {
1166: nameTF.setText(cd.name);
1167: objCB.setSelectedIndex(cd.objectKind);
1168: //kindCB.setSelectedIndex( cd.fileKind );
1169: freqCB.setSelectedIndex(cd.freq);
1170: target.setPath(cd.target);
1171: targetURLTF.setText(cd.target);
1172: recursionCB.setSelectedIndex(cd.recursionDepth);
1173:
1174: setFileVisible(objCB.getSelectedIndex() == 0);
1175: }
1176:
1177: private void getData(final WatchedData cd) //throws Exception
1178: {
1179: cd.name = nameTF.getText();
1180: cd.objectKind = objCB.getSelectedIndex();
1181: //cd.fileKind = kindCB.getSelectedIndex();
1182: cd.freq = freqCB.getSelectedIndex();
1183: if (objCB.getSelectedIndex() == 0) {
1184: cd.target = target.getTextField().getText().trim();
1185: } else {
1186: cd.target = targetURLTF.getText();
1187: }
1188: cd.recursionDepth = recursionCB.getSelectedIndex();
1189: }
1190: }
1191:
1192: /** Tricky. tricky.
1193: * IF the url last modified exists => returns it as is.
1194: * IF the content length exists => returns it.
1195: * otherwise, reads the whole content and return the length.
1196: *
1197: * this may only be used as a simple hash to detect changes.
1198: */
1199: public static long computeHashForURLContent(URL url)
1200: throws Exception {
1201: final URLConnection uRLConnection = url.openConnection();
1202: uRLConnection.setAllowUserInteraction(true);
1203: long lm = uRLConnection.getLastModified();
1204: //System.out.println("last mod= "+ lm);
1205: if (lm > 0)
1206: return lm;
1207: //System.out.println("date = "+ new Date(uRLConnection.getDate()));
1208:
1209: long len = uRLConnection.getContentLength();
1210: if (len > 0)
1211: return len;
1212: //System.out.println("headerfields="+uRLConnection.getHeaderFields());
1213:
1214: /* Object cont = uRLConnection.getContent();
1215: System.out.println("\n\nCont = "+cont);*/
1216:
1217: if (uRLConnection instanceof HttpsURLConnection) {
1218: HttpsURLConnection httpscon = (HttpsURLConnection) uRLConnection;
1219:
1220: //httpscon.;
1221: System.out.println("HTTPS type");
1222: httpscon.setHostnameVerifier(new HostnameVerifier() {
1223: public final boolean verify(final String hostname,
1224: final SSLSession session) {
1225: return true;
1226: }
1227: });
1228:
1229: final InputStream inputStream = httpscon.getInputStream();
1230:
1231: long read = 0;
1232: int rd = 0;
1233: byte[] buf = new byte[128];
1234: while ((rd = inputStream.read(buf)) != -1) {
1235: read += rd;
1236: }
1237: //System.out.println("read="+read);
1238:
1239: return read;
1240: } else if (uRLConnection instanceof HttpURLConnection) {
1241:
1242: HttpURLConnection httpURLConnection = (HttpURLConnection) uRLConnection;
1243: final InputStream inputStream = httpURLConnection
1244: .getInputStream();
1245:
1246: long read = 0;
1247: int rd = 0;
1248: byte[] buf = new byte[128];
1249: while ((rd = inputStream.read(buf)) != -1) {
1250: read += rd;
1251: }
1252: //System.out.println("read="+read);
1253:
1254: return read;
1255: } else {
1256: System.out.println("unknown CON="
1257: + uRLConnection.getClass().getName());
1258: return -1;
1259: }
1260: }
1261:
1262: public static void main(String[] args) {
1263: // long value = Integer.MAX_VALUE+1;
1264: // System.out.println(""+(value % Integer.MAX_VALUE)); // -1 !!
1265:
1266: standalone = true;
1267: getInstance().setVisible(true);
1268: }
1269:
1270: }
|