001 /** 002 * Copyright 2005-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.devtools.datadictionary; 017 018 import org.apache.commons.logging.Log; 019 import org.apache.commons.logging.LogFactory; 020 021 import java.io.IOException; 022 import java.net.URL; 023 import java.util.LinkedList; 024 import java.util.Map; 025 import java.util.concurrent.ConcurrentHashMap; 026 import java.util.concurrent.Executors; 027 import java.util.concurrent.ScheduledExecutorService; 028 import java.util.concurrent.TimeUnit; 029 import java.util.zip.CRC32; 030 import java.util.zip.CheckedInputStream; 031 032 /** 033 * This is a description of what this class does - gilesp don't forget to fill this in. 034 * 035 * @author Kuali Rice Team (rice.collab@kuali.org) 036 * 037 */ 038 public class URLMonitor { 039 private static final Log LOG = LogFactory.getLog(URLMonitor.class); 040 041 private final LinkedList<URLContentChangedListener> listeners = new LinkedList<URLContentChangedListener>(); 042 private final Map<URL, Long> resourceMap = new ConcurrentHashMap(); 043 private final int reloadIntervalMilliseconds; 044 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 045 046 047 public URLMonitor(int reloadIntervalMilliseconds) { 048 this.reloadIntervalMilliseconds = reloadIntervalMilliseconds; 049 } 050 051 public void shutdownScheduler() { 052 scheduler.shutdown(); 053 } 054 055 public synchronized void addListener(URLContentChangedListener listener) { 056 057 listeners.add(listener); 058 059 if (listeners.size() == 1) { 060 scheduler.scheduleAtFixedRate(urlPoller, 061 reloadIntervalMilliseconds, reloadIntervalMilliseconds, TimeUnit.MILLISECONDS); 062 } 063 } 064 065 public void addURI(URL zipUrl) { 066 067 resourceMap.put(zipUrl, getCRC(zipUrl)); 068 } 069 070 private Long getCRC(URL zipUrl) { 071 Long result = -1l; 072 try { 073 CRC32 crc = new CRC32(); 074 CheckedInputStream cis = new CheckedInputStream(zipUrl.openStream(), crc); 075 076 byte[] buffer = new byte[1024]; 077 int length; 078 079 //read the entry from zip file and extract it to disk 080 while( (length = cis.read(buffer)) > 0); 081 082 cis.close(); 083 084 result = crc.getValue(); 085 } catch (IOException e) { 086 LOG.warn("Unable to calculate CRC, resource doesn't exist?", e); 087 } 088 return result; 089 } 090 091 private final Runnable urlPoller = new Runnable() { 092 093 @Override 094 public void run() { 095 for (Map.Entry<URL, Long> entry : resourceMap.entrySet()) { 096 Long crc = getCRC(entry.getKey()); 097 098 if (!entry.getValue().equals(crc)) { 099 entry.setValue(crc); 100 for (URLContentChangedListener listener : listeners) { 101 listener.urlContentChanged(entry.getKey()); 102 } 103 } 104 } 105 } 106 }; 107 108 public static interface URLContentChangedListener { 109 public void urlContentChanged(URL url); 110 } 111 }