Электронный магазин на Java и XML

Служебная библиотека документа XML


Существует альтернативный подход к тому, чтобы обеспечить каждому сервлету доступ к соответствующему документу XML. Этот подход заключается в использовании служебных библиотек. В этом случае сервлет просто запрашивает документ из библиотеки независимо от того, находится ли файл XML на диске и где именно или он уже вызван в результате какого-либо другого запроса и находится в памяти.

Ниже перечислены характеристики созданного нами класса DOMlibrary.

Построен по шаблону единичного класса (singleton), который допускает создание только одного экземпляра класса. В такой схеме отсутствует открытый (public) конструктор, вместо него имеется статический метод, контролирующий создание единичного экземпляра класса и доступ к этому экземпляру.

Когда поступает запрос на документ XML, экземпляр DOMlibrary проверяет время создания файла, содержащего документ. Это гарантирует, что в ответ на запрос не будет выдан устаревший документ.

Реализует интерфейс Runnable, поэтому в нем может содержаться объект Thread, который периодически выполняет некоторые вспомогательные функции. Типичной вспомогательной функцией является исключение из памяти объектов Document, которые давно не использовались. Таким образом, постоянно задействованные документы, как правило, окажутся в памяти, в то время как редко используемые документы не будут занимать память, истощая ее ресурсы.

Вместо того чтобы использовать единичный класс, можно было бы реализовать все только через статические методы. Однако благодаря шаблону единичного класса мы выигрываем в отношении гибкости, получая, в частности, возможность реализовать интерфейс Runnabl e и использовать метод run для управления жизненным циклом документа в памяти. Шаблон единичного класса очень часто можно встретить в стандартной библиотеке Java.

В листинге 7.37 показан статический метод getLibrary, который при необходимости создает новый объект DOMlibrary. Все сервлеты, которым требуется доступ к документу XML, вызывают метод getLibrary для получения ссылки на единственный экземпляр библиотеки, а затем с помощью этой ссылки запрашивают нужный документ. Переменная maxAge используется в методе run для того, чтобы определить, когда документ следует убрать из памяти.




Листинг 7.37. Инструкции импорта и статические методы класса DOMlibrary (DOMIibrary.java)

package com.XmlEcomBook ;

import java.io.* ; import java.util.* ; import com.sun.xml.tree.* ; import com.sun.xml.parser.Resolver ; import org.xml.sax.* ; import org.w3c.dom.* ;

public class DOMlibrary implements java.lang.Runnable { private static DOMlibrary theLib ; private static int maxAge = 6000 ; // age in seconds public synchronized static DOMlibrary getLibrary(){ if( theLib == null ) theLib = new DOMlibrary(); return theLib ; } public static void setMaxAge(int t) { maxAge = t ;}



Как показано в листинге 7.38, единственный конструктор является закрытым (private), чтобы гарантировать, что только лишь статический метод getLibrary может создать новый объект. Резидентные объекты XML document хранятся в хэш- таблице domHash; ключом является путь к соответствующему файлу. Хэш-табли- ца с именем trackerHash, используя тот же ключ, сохраняет объект DomTracker для каждого объекта XML document. Класс DOMTracker — внутренний класс в DOMlibrary; его код приведен в листинге 7.43. Обратите внимание на то, что объекту Thread (потоку, выполняющему метод run), присвоен самый низкий приоритет.



Листинг 7.38. Конструктор и переменные экземпляра класса DOMlibrary (DOMIibrary.java)

private Hashtable domHash, trackerHash ; boolean running ; private String lastErr = "none" ; // private constructor to ensure singleton private DOMlibrary(){ domHash = new Hashtable(); trackerHash = new Hashtable(); Thread upkeep = new Thread(this,"DOMlibrary upkeep"); upkeep.setPriority( Thread.MIN_PRIORITY ); running = true ; upkeep.start(); }

Анализ документа XML в DOMlibrary осуществляется в методе loadXML, как показано в листинге 7.39. Чтобы избежать многократных попыток загрузить документ с неверно указанным атрибутом scr (путь к файлу) или документ, загрузка которого вызывает синтаксическую ошибку, этот метод помещает в таблицу domHash строку, содержащую сообщение об ошибке, если таковая встречается. Если анализ документа проходит успешно, в таблицу trackerHash записывается соответствующий объект DomTracker. Это единственный метод, в котором вызываются специфические для анализа документов методы; если бы вы вместо анализатора Sun использовали для анализа что-либо другое, вам потребовалось бы несколько модифицировать этот метод.





Листинг 7.39. Метод loadXML осуществляет анализ документа XML (DOMIibrary.java)

private synchronized void loadXML(File xmlFile, String src ) { //File xmlFile = new File( src ) ; try { long timestamp = xmlFile.lastModified(); InputSource input = Resolver.createInputSource( xmlFile ); // ... the "false" flag says not to validate (faster) // XmlDocument is in the com.sun.xml.tree package Document doc = XmlDocument.createXmlDocument (input, false); domHash.put( src, doc ); trackerHash.put( src, new DomTracker( timestamp ) ); }catch(SAXParseException spe ){ StringBuffer sb = new StringBuffer( spe.toString() ); sb.append("\n Line number: " + spe.getLineNumber()); sb.append("\nColumn number: " + spe.getColumnNumber() ); sb.append("\n Public ID: " + spe.getPublicId() ); sb.append("\n System ID: " + spe.getSystemId() + "\n"); lastErr = sb.toString(); System.out.print( lastErr ); }catch( SAXException se ){ lastErr = se.toString(); System.out.println("loadXML threw " + lastErr ); domHash.put( src, lastErr ); se.printStackTrace( System.out ); }catch( IOException ie ){ lastErr = ie.toString(); System.out.println("loadXML threw " + lastErr + " trying to read " + src ); domHash.put( src, lastErr ); } } // end loadXML

Когда сервлетам требуется получить документ, они вызывают метод getDOM, показанный в листинге 7.40. Если при создании документа возникают какие-либо проблемы, то вместо ссылки на документ этот метод возвращает null. Каждый раз, когда в хэш-таблице обнаруживается требуемый документ, в ассоциированном объекте DomTracker обновляется значение времени последнего использования, которое заменяется текущим временем. Заметим, что в нескольких местах создаются выходные сообщения, которые записываются в объект System.out и служат для отладки. Мы советует закомментировать их после того, как система заработает.



Листинг 7.40. Метод getDOM (DOMIibrary.java)

// either return the doc or null if a problem public synchronized Document getDOM( String src ){ Object doc = domHash.get( src ); DomTracker dt = (DomTracker) trackerHash.get( src ); boolean newflag = false ; File f = null ; if( doc == null ){ System.out.println("DOMlibrary.getDOM new " + src ); f = new File( src ); loadXML( f, src ); // sets trackerHash doc = domHash.get( src ); dt = (DomTracker) trackerHash.get( src ); newflag = true ; System.out.println("DOMlibrary load OK"); } else { // found a document - is it up to date? f = new File( src ); if( dt.changed( f )){ System.out.println("DOMlibrary reloads " + src ); loadXML( f, src ); // sets trackerHash newflag = true ; doc = domHash.get( src ); dt = (DomTracker)trackerHash.get( src ); } } // if not a document, must be a string due to error if( ! (doc instanceof Document )){ System.out.println("DOMlibrary: " + doc ); // could try for re-read here } if( doc instanceof Document ) { if( ! newflag ){ dt = (DomTracker)trackerHash.get( src ); dt.setLastUse( System.currentTimeMillis()); } return (Document) doc ; } return null ; }



В листинге 7. 41 представлена пара служебных методов, которые используются для удаления документа из памяти или для его перезагрузки.



Листинг 7.41. Некоторые служебные методы (DOMIibrary.java)

// use this to force removal of a dom. it // returns last copy of dom or null if dom not in hash public synchronized Document removeDOM( String src ){ Document dom = (Document)domHash.get( src ); if( dom != null ){ domHash.remove( src ); trackerHash.remove( src ); // System.out.println("Removed " + src ); } return dom ; }

// call this to force a reload after src is modified public synchronized Document reloadDOM( String src ){ if( domHash.get( src ) != null ){ domHash.remove( src ); trackerHash.remove( src ); } return getDOM( src ); }

Класс DOMlibrary должен реализовывать интерфейс Runnable, чтобы можно было использовать в фоновом режиме поток, имеющий минимальный приоритет и выполняющий служебные функции. Пример, приведенный в листинге 7.42, очень прост: поток удаляет все документы, которые давно не используются. Также в листинге 7.42 показаны служебные методы toString и getLastErr.



Листинг 7.42. Метод run и другие служебные методы (DOMIibrary.java)

// run is used for upkeep, not reading XML public void run() { while( running ){ try{ Thread.sleep( 60000 ); // example management code Enumeration keys = trackerHash.keys(); long time = System.currentTimeMillis(); while( keys.hasMoreElements() ){ String key = (String) keys.nextElement(); if(((DomTracker)trackerHash.get(key)).getAge(time) > maxAge ){ removeDOM( key ); } } }catch(InterruptedException e){ } }// end while }

public String getLastErr(){ return lastErr ; }

public String toString() { StringBuffer sb = new StringBuffer("DOMlibrary contains "); int ct = domHash.size(); if( ct > 0 ){ sb.append(Integer.toString( ct ) ); sb.append( " DOM objects "); Enumeration e = domHash.keys(); while( e.hasMoreElements() ){ String key = (String)e.nextElement(); sb.append( key ); sb.append(" " ); } } else { sb.append("no DOM objects"); } sb.append(" Last error: " ); sb.append( lastErr ); return sb.toString(); }



Экземпляр внутреннего класса DOMTracker создается всякий раз, когда загружается документ XML. Этот экземпляр подвергается обработке параллельно с самим объектом document. В нынешней версии нас интересуют только два параметра: время, когда документ XML был создан, и время, когда последний раз поступал запрос на этот документ. Как показано в листинге 7.43, метод getAge возвращает время в секундах, прошедшее с момента последнего использования документа, а метод changed проверяет время создания или последней модификации исходного файла.



Листинг 7.43. Определение класса DOMTracker как члена класса DOMIibrary (DOMIibrary.java)

// utility class to aid in tracking memory resident DOM class DomTracker { private long lastMod ; private long lastUse ; DomTracker( long timestamp ){ lastMod = timestamp ; // from File.lastModified(); lastUse = System.currentTimeMillis(); } void setLastUse( long ts ){ lastUse = ts ; } int getAge( long now ){ // return value in seconds return (int)(( now - lastUse)/ 1000) ; } boolean changed( File f ){ long n = f.lastModified(); return !( n == lastMod ); } } }

Используя инструментальные средства, описанные в этой главе, вы можете создать гибкую систему получения информации о ваших покупателях — как потенциальных, так и уже покупающих товары в вашем магазине. Поскольку эта система использует сценарий, основанный на XML и допускающий возможность изменения без перезапуска сервера, вы можете направлять опрос в то или иное русло в зависимости от мнений, которые высказывает клиент по ходу опроса.


Содержание раздела