View Javadoc

1   /* $Id: RegistryQuerierImpl.java,v 1.7 2005/01/23 12:52:33 jdt Exp $
2    *
3    * Copyright (C) AstroGrid. All rights reserved.
4    *
5    * This software is published under the terms of the AstroGrid 
6    * Software License version 1.2, a copy of which has been included 
7    * with this distribution in the LICENSE.txt file.  
8    *
9    * Created on Jul 29, 2004
10   */
11  // This class needs a registry to be tested
12  // It is tested by the Integration Tests, so we
13  // don't need to include it in the clover stats.
14  ///CLOVER:OFF 
15  package org.astrogrid.applications.http.registry;
16  
17  import java.io.IOException;
18  import java.net.URL;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import junit.framework.Test;
25  import junit.framework.TestCase;
26  import junit.framework.TestSuite;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.astrogrid.applications.beans.v1.types.ApplicationKindType;
31  import org.astrogrid.config.ConfigException;
32  import org.astrogrid.config.PropertyNotFoundException;
33  import org.astrogrid.config.SimpleConfig;
34  import org.astrogrid.registry.RegistryException;
35  import org.astrogrid.registry.beans.cea.CeaHttpApplicationType;
36  import org.astrogrid.registry.beans.resource.IdentifierType;
37  import org.astrogrid.registry.client.RegistryDelegateFactory;
38  import org.astrogrid.registry.client.query.RegistryService;
39  import org.astrogrid.util.DomHelper;
40  import org.exolab.castor.xml.CastorException;
41  import org.exolab.castor.xml.MarshalException;
42  import org.exolab.castor.xml.Unmarshaller;
43  import org.exolab.castor.xml.ValidationException;
44  import org.w3c.dom.Document;
45  import org.w3c.dom.Element;
46  import org.w3c.dom.NodeList;
47  
48  /***
49   * A RegistryQuerier that gets generates a list of Http applications from an
50   * actual registry. Much of the code pinched from Workflow's
51   * RegistryApplicationRegistry. Handles any transformation from objects/docs in
52   * the registry to the format we want to work with in the CEA.
53   * 
54   * @author jdt
55   */
56  public class RegistryQuerierImpl implements RegistryQuerier {
57      private static final Log log = LogFactory.getLog(RegistryQuerierImpl.class);
58  
59      private RegistryService service;
60  
61      /***
62       * ctor allowing delegate to find own registry end-point
63       */
64      public RegistryQuerierImpl() {
65          if (log.isTraceEnabled()) {
66              log.trace("RegistryQuerierImpl() - start");
67          }
68  
69          service = RegistryDelegateFactory.createQuery();
70          assert service != null;
71  
72          if (log.isTraceEnabled()) {
73              log.trace("RegistryQuerierImpl() - end");
74          }
75      }
76  
77      /***
78       * Construct a new RegistryQuerierImpl
79       * 
80       * @param endpoint endpoint for the astrogrid registry web service
81       */
82      public RegistryQuerierImpl(URL endpoint) {
83          if (log.isTraceEnabled()) {
84              log.trace("RegistryQuerierImpl(URL endpoint = " + endpoint + ") - start");
85          }
86  
87          service = RegistryDelegateFactory.createQuery(endpoint);
88          assert service != null;
89  
90          if (log.isTraceEnabled()) {
91              log.trace("RegistryQuerierImpl(URL) - end");
92          }
93      }
94  
95      /***
96       * Query string to select only CeaHttpApplications in the registry
97       */
98      private final static String LIST_QUERY_STRING =   "<query><selectionSequence>" +
99      "<selection item='searchElements' itemOp='EQ' value='vr:Resource'/>" +
100     "<selectionOp op='$and$'/>" +
101     "<selection item='@xsi:type' itemOp='EQ' value='CeaHttpApplicationType'/>"  +
102     "<selectionOp op='OR'/>" +
103     "<selection item='@xsi:type' itemOp='EQ' value='cea:CeaHttpApplicationType'/>"  +
104     "</selectionSequence></query>";
105    
106 
107     /***
108      * Get the names of all the applications in the registry matching
109      * LIST_QUERY_STRING An empty List could indicate entries in the registry
110      * aren't in the right format and can't be unmarshalled.
111      * 
112      * @return List of application name strings
113      * @throws RegistryException on registry comms probs
114      */
115     protected List listApplications() throws RegistryException {
116         if (log.isTraceEnabled()) {
117             log.trace("listApplications() - start");
118         }
119 
120         try {
121             log.debug("Querying registry: "+LIST_QUERY_STRING);
122             Document doc = service.submitQuery(LIST_QUERY_STRING);
123             assert doc != null;
124             NodeList nl = doc.getElementsByTagNameNS("*", "Identifier");
125             log.debug("...got doc with "+nl.getLength()+" Identifier elements");
126             List namesList = new ArrayList();
127             for (int i = 0; i < nl.getLength(); i++) {
128                 IdentifierType it;
129                 try {
130                     it = (IdentifierType) Unmarshaller.unmarshal(IdentifierType.class, nl.item(i));
131                     String name = it.getAuthorityID() + "/" + it.getResourceKey();
132                     namesList.add(name);
133                     log.debug("Adding "+name+" to list of applications");
134                 } catch (MarshalException e1) {
135                     log.error("listApplications(): problem unmarshalling " + nl.item(i) + " cannot add to list", e1);
136                 } catch (ValidationException e1) {
137                     log.error("listApplications(): problem validating " + nl.item(i) + " cannot add to list", e1);
138                 }
139             }
140 
141             if (log.isTraceEnabled()) {
142                 log.trace("listApplications() - end - return value = " + namesList);
143             }
144             return namesList;
145         } catch (RegistryException e) {
146             log.error("listApplications Failed with exception from registry", e);
147             throw e;
148         }
149     }
150 
151     /***
152      * Get application by name from the registry
153      * 
154      * @see org.astrogrid.portal.workflow.intf.ApplicationRegistry#getDescriptionFor(java.lang.String)
155      * @param applicationName application name in registry as returned by
156      *            listApplications()
157      * @return a WebHttpApplication containing all the details stored in the
158      *         registry
159      * @throws CastorException castor unmarshalling probs
160      * @throws RegistryCallException on registry comms probs
161      */
162     private CeaHttpApplicationType getDescriptionFor(String applicationName) throws RegistryException, CastorException {
163         if (log.isTraceEnabled()) {
164             log.trace("getDescriptionFor(String applicationName = " + applicationName + ") - start");
165         }
166 
167         try {
168             final Document doc = service.getResourceByIdentifier(applicationName);
169             assert doc != null;
170 
171             final CeaHttpApplicationType webApplication = (CeaHttpApplicationType) unmarshal(doc,
172                     CeaHttpApplicationType.class, "Resource");
173             
174             if (log.isTraceEnabled()) {
175                 log.trace("getDescriptionFor(String) - end - return value = " + webApplication);
176             }
177             return webApplication;
178         } catch (CastorException e) {
179             log.error("getDescriptionFor " + applicationName + " - castor failed to parse result", e);
180             throw e;
181         } catch (RegistryException e) {
182             log.error("getDescriptionFor " + applicationName + " - exception from registry", e);
183             throw e;
184         }
185     }
186     
187 
188     /***
189      * @see org.astrogrid.component.descriptor.ComponentDescriptor#getInstallationTest()
190      */
191     public Test getInstallationTest() {
192         TestSuite suite = new TestSuite();
193         suite.addTest(new InstallationTest("testRegistryEndPointPropertyFound"));
194         suite.addTest(new InstallationTest("testRegistryServiceFound"));
195         suite.addTest(new InstallationTest("testRegistryContainsOneOrMoreHttpApplications"));
196         return suite;
197     }
198     /***
199      * Some installation tests.
200      * There's a certain amount of redundancy here (test 2 will fail if test 1 does etc)
201      * but the tests are performed separately to make problems
202      * clearer to the user.
203      * @author jdt
204      */
205     public class InstallationTest extends TestCase {
206         public InstallationTest(String arg0) {
207             super(arg0);
208         }
209         /***
210          * Can we find the endpoint of the registry in properties?
211          * Not convinced this should be here, but it has
212          * the side effect that it helps us determine if *any*
213          * properties can be found 
214          *
215          */
216         public void testRegistryEndPointPropertyFound() {
217             //@TODO in SNAPSHOT get this from RegistryDelegateFactory
218             //RegistryDelegateFactory.;
219             String key = "org.astrogrid.registry.query.endpoint";
220             try {
221                 String endPoint = SimpleConfig.getProperty(key);
222                 assertNotNull("The Property "+key+" is not set - check your configuration file");
223             } catch (ConfigException e ) {
224                 fail("There was a problem accessing the configuration file: "+e.getMessage());
225             } catch (PropertyNotFoundException e) {
226                 fail("The Property "+key+" is not set - check your configuration file");
227             }
228         }
229         public void testRegistryServiceFound() throws RegistryException {
230             assertNotNull("No registry service found", service);
231             // The registry client seems to return a non-null service
232             // even when it is up.  The following is a hack that should
233             // fail when the registry is down.
234             // @TODO replace with something more elegant when the registry allows
235             Object doc = service.loadRegistry();
236             assertNotNull("No registry service found.  Check that your registry is up and running and that the property org.astrogrid.registry.query.endpoint is set correctly.", doc);
237         }
238         public void testRegistryContainsOneOrMoreHttpApplications() throws IOException {
239             Collection apps = getHttpApplications();
240             assertFalse("There were no http applications in the registry.   Check your registry entries and this error might go away.", apps.isEmpty());
241         }
242 
243     }
244     /***
245      * Unmarshall a node of a Document into a class
246      * 
247      * @param doc the document
248      * @param clazz the class you hope to get back
249      * @param elementName the element name in the document you hope matches the
250      *            class
251      * @return object that will need casting...
252      * @throws MarshalException
253      * @throws ValidationException
254      * @throws RegistryCallException
255      */
256     private Object unmarshal(final Document doc, final Class clazz, final String elementName) throws MarshalException,
257             ValidationException, RegistryException {
258         if (log.isTraceEnabled()) {
259             log.trace("unmarshal(Document doc = " + doc + ", Class clazz = " + clazz + ", String elementName = "
260                     + elementName + ") - start");
261         }
262 
263         if (log.isDebugEnabled()) {
264         	log.debug("Unmarshalling document:");
265         	log.debug(DomHelper.DocumentToString(doc));
266         	log.debug("=======================");
267         }
268         //      navigate down to the bit we're interested in.
269         final NodeList nls = doc.getElementsByTagNameNS("*", elementName);
270         if (nls.getLength() == 0) {
271             throw new RegistryException("Registry entry has no " + elementName + " Element");
272         }
273         final Element ns = (Element) nls.item(0);
274         ns.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); //bug-fix work around.
275         Object returnObject = Unmarshaller.unmarshal(clazz, ns);
276         if (log.isTraceEnabled()) {
277             log.trace("unmarshal(Document, Class, String) - end - return value = " + returnObject);
278         }
279         return returnObject;
280     }
281 
282     /***
283      * Returns a List of WebHttpApplications from the Registry. Note, that if
284      * this method returns an empty list, it might indicate communication
285      * problems with the registry, or that the registry entries are invalid and
286      * not unmarshalable
287      * 
288      * @throws
289      * @see org.astrogrid.applications.http.registry.RegistryQuerier#getHttpApplications()
290      */
291     public Collection getHttpApplications() throws IOException {
292         if (log.isTraceEnabled()) {
293             log.trace("getHttpApplications() - start");
294         }
295         
296 
297         try {
298             List apps = new ArrayList();
299 
300             List names = listApplications();
301             log.debug("Got a list of "+names.size()+" applications");
302             Iterator it = names.iterator();
303             while (it.hasNext()) {
304                 String name = (String) it.next();
305                 try {
306                     final CeaHttpApplicationType description = getDescriptionFor(name);
307                     ApplicationKindType type=description.getApplicationDefinition().getApplicationKind();
308                     if (ApplicationKindType.HTTP.equals(type)) {
309                         apps.add(description);
310                         log.debug("Adding "+description.getShortName()+" to list");
311                     } else {
312                         log.warn("Description "+description.getShortName()+" is listed in the registry as a CeaHttpApplication, but is actually a "+type+" app.  Must be an http app.");
313                         log.warn("=>not adding to list");
314                     }
315                 } catch (CastorException e) {
316                     log.info("getHttpApplications(): problem unmarshalling " + name + ", not adding to list", e);
317                 }
318             }
319 
320             if (log.isTraceEnabled()) {
321                 log.trace("getHttpApplications() - end - return value = " + apps);
322             }
323             return apps;
324         } catch (RegistryException e) {
325             log.error("getHttpApplications(): RegistryException", e);
326             throw new IOException("Problem communicating with Registry");
327         }
328     }
329 
330     public String toString() {
331         if (log.isTraceEnabled()) {
332             log.trace("toString() - start");
333         }
334 
335         StringBuffer buffer = new StringBuffer();
336         buffer.append("[RegistryQuerierImpl:");
337         buffer.append(" log: ");
338         buffer.append(log);
339         buffer.append(" service: ");
340         buffer.append(service);
341         buffer.append(" LIST_QUERY_STRING: ");
342         buffer.append(LIST_QUERY_STRING);
343         buffer.append("]");
344         String returnString = buffer.toString();
345         if (log.isTraceEnabled()) {
346             log.trace("toString() - end - return value = " + returnString);
347         }
348         return returnString;
349     }
350 
351     /* (non-Javadoc)
352      * @see org.astrogrid.component.descriptor.ComponentDescriptor#getName()
353      */
354     public String getName() {
355         return "RegistryQuerierImpl";
356     }
357 
358     /* (non-Javadoc)
359      * @see org.astrogrid.component.descriptor.ComponentDescriptor#getDescription()
360      */
361     public String getDescription() {
362         return "Talks to the registry to get http applications";
363     }
364 }