Главный управляющий класс
Теперь мы переходим к рассмотрению класса, который управляет процессом получения заголовков через определенные промежутки времени и обеспечивает доступ к ним сервлетов. Класс NetNewsSuper устроен согласно обычной схеме единичного класса (singleton). В нем имеются статические переменные и методы, которые гарантируют, что для каждого источника XML (URL-адрес вместе с именами папки и файла) создается только один экземпляр NetNewsSuper. Как показано в листинге 9.19, этот URL-адрес используется в качестве ключа для получения экземпляра NetNewsSuper из хэш-таблицы nnsHash или для его создания, если он еще не существует.
Листинг 9.19. Начало класса NetNewsSuper (NetNewsSuper.java) package com.XmlEcomBook.Chap09;
package com.XmlEcomBook.Chap09;
import java.util.*; import java.io.* ;
public class NetNewsSuper extends java.lang.Thread { static Hashtable nnsHash = new Hashtable() ; static long classLoaded = System.currentTimeMillis(); static long longTime = 1000 * 60 * 60 * 2 ;// two hours static int maxErrCt = 10 ; // source is URL + query, dest is abs //file path, destFname = name // hash stored by complete source string as key static synchronized NetNewsSuper getNetNewsSuper(String source, String destPth, String destFname ){ NetNewsSuper nns = (NetNewsSuper)nnsHash.get( source ); if( nns == null ){ nns = new NetNewsSuper( source, destPth, destFname ) ; nnsHash.put( source, nns ); } return nns ; }
static synchronized void removeNetNewsSuper( String sourceURL ){ Object obj = nnsHash.remove( sourceURL ); if( obj == null ){ System.out.println("removeNetNewsSuper of " + sourceURL + " failed." ); } else { // allow Thread to die ((NetNewsSuper)obj).running = false ; } }
Причина, по который мы сделали класс NetNewsSuper расширением класса Thread, вместо того чтобы реализовать интерфейс Runnable, связана с удобством отладки и управления набором сервлетов. Самое простое — это написать сервлет, который перечисляет все работающие потоки (объекты Thread) в виртуальной машине процессора сервлетов. Вы можете отобразить и имя экземпляра, и результат вызова метода toString.
Как видно из кода конструктора класса, приведенного в листинге 9.20, имя потока устроено таким образом, что включает в себя имя извлекаемого файла. Когда вызывается .конструктор, ему передаются URL-адрес источника, имя папки и имя файла, которые используются для записи данных XML. Поскольку предполагается, что получение нового набора заголовков будет происходить в фоновом режиме, то данный поток получает минимальный приоритет.
Листинг 9.20. Переменные экземпляра и конструктор класса NetNewsSuper (NetNewsSuper.java)
// instance variables and methods follow String sourceURL ; String destPath, destFname ; public String errStr ; public boolean usable ; int errCt = 0 ; boolean running ;
NewsModel newsM ; private NetNewsSuper (String source, String dest, String fname ){ sourceURL = source ; destPath = dest ; destFname = fname ; setName("NetNewsSuper " + fname ); setPriority( Thread.MIN_PRIORITY ); start(); System.out.println("NetNewsSuper Thread started"); }
Как показано в листинге 9.21, первое, что делает метод run, — вызывает метод checkSrc, который проверяет, существует ли уже требуемый файл XML. Если это не так, создается объект класса XMLgrabber и выполняется его метод doQueryNow для получения исходного файла XML. Если файл XML присутствует, то выполняется метод createModel, который создает новый объект NewsModel.
Листинг 9.21. Метод run класса NetNewsSuper (NetNewsSuper.java)
// low priority - check for need to update xml public void run(){ running = true ; try { // runs when first started if( !checkSrc() ){ XMLgrabber grab = new XMLgrabber ( sourceURL, destPath, destFname ); System.out.println("NetNewsSuper runs doQueryNow"); if( !grab.doQueryNow() ){ errCt++ ; System.out.println ("NetNewsSuper.run - bad return from grab"); } } createModel(); }catch(Exception e1){ errCt++ ; } while( running ){ try { sleep( longTime ); XMLgrabber grab = new XMLgrabber ( sourceURL, destPath, destFname ); //System.out.println("NetNewsSuper.run runs doQueryNow"); if( running && grab.doQueryNow() ){ if( errCt > 0 ) errCt-- ; createModel(); } else { errCt++ ; System.out.println ("NetNewsSuper.run - bad return from grab"); } }catch(InterruptedException ie){ errCt++ ; System.err.println("NetNewsSuper.run " + ie ); }catch(Exception ee ){ errCt++ ; System.err.println("NetNewsSuper.run " + ee ); } if( errCt > maxErrCt ){ System.out.println ("NetNewsSuper.run too many errors: " + errCt +" run exiting."); running = false ; } } System.out.println("Leaving NetNewsSuper.run method"); }
// return true if XML source file is found private boolean checkSrc(){ File f = new File( destPath, destFname ); return (f.exists() && f.canRead()); }
В листинге 9.22 показан метод, который создает новый экземпляр класса NewsModel и затем вызывает методы экземпляра loadXML и locateCategories. В случае ошибки переменная usabl е устанавливается равной fal se.
Листинг 9.22. Этот метод создает новый объект NewsModel (NetNewsSuper.java)
// xml source known to exist, go for it private synchronized void createModel(){ newsM = new NewsModel( destPath, destFname ); if( !newsM.loadXML()){ // error in getting data errStr = newsM.lastErr ; usable = false ; } else { newsM.locateCategories(); usable = true ; } }
Как мы увидим при обсуждении в следующем разделе классов NetNewsBean и NetNewsServ и как показано в листинге 9.23, сервлет запрашивает текущую модель NewsModel с помощью метода getNewsModel. Если она отсутствует, что может случиться из-за сбоя в сети, который прерывает нормальную работу метода run, метод getNewsModel делает попытку получить NewsModel заново.
Листинг 9.23. Метод getNewsModel возвращает NewsModel (NetNewsSuper.java)
// Note that there are two steps to getting a news //model resident: // 1. grabbing the current XML to local file if not there already // 2. creating the NewsModel from the local XML public synchronized NewsModel getNewsModel() throws Exception { if( newsM != null ) return newsM ; // must be newly created NetNewsSuper if( !checkSrc() ){ XMLgrabber grab = new XMLgrabber( sourceURL, destPath, destFname ); //System.out.println("getNewsModel runs doQueryNow"); if( !grab.doQueryNow() ){ // System.out.println(" bad return from grab"); return null ; } } // source exists, create model createModel(); return newsM ; // may or may not be usable }
Метод toString, как показано в листинге 9.24, предоставляет краткую сводку о текущем состоянии объекта NetNewSuper.
Листинг 9.24. Метод toString (NetNewsSuper.java)
public String toString() { StringBuffer sb = new StringBuffer( "NetNewsSuper for "); sb.append( sourceURL ); if( newsM == null ){ sb.append(" No NewsModel resident "); } else { sb.append(" NewsModel resident, status: " + usable ); } sb.append(" class loaded: " ); sb.append( new Date( classLoaded ).toString() ); return sb.toString() ; }
}