View Javadoc

1   /* $Id: RegistryQuerierImpl.java,v 1.8 2005/03/02 12:38:46 clq2 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      /*
99      private final static String LIST_QUERY_STRING =   "<query><selectionSequence>" +
100     "<selection item='searchElements' itemOp='EQ' value='vr:Resource'/>" +
101     "<selectionOp op='$and$'/>" +
102     "<selection item='@xsi:type' itemOp='EQ' value='CeaHttpApplicationType'/>"  +
103     "<selectionOp op='OR'/>" +
104     "<selection item='@xsi:type' itemOp='EQ' value='cea:CeaHttpApplicationType'/>"  +
105     "</selectionSequence></query>";
106     */
107     private final static String LIST_QUERY_STRING = "Select * from Registry " + 
108     " where @xsi:type='cea:CeaHttpApplicationType'";
109    
110 
111     /***
112      * Get the names of all the applications in the registry matching
113      * LIST_QUERY_STRING An empty List could indicate entries in the registry
114      * aren't in the right format and can't be unmarshalled.
115      * 
116      * @return List of application name strings
117      * @throws RegistryException on registry comms probs
118      */
119     protected List listApplications() throws RegistryException {
120         if (log.isTraceEnabled()) {
121             log.trace("listApplications() - start");
122         }
123 
124         try {
125             log.debug("Querying registry: "+LIST_QUERY_STRING);
126             //Document doc = service.submitQuery(LIST_QUERY_STRING);
127             Document doc = service.searchFromSADQL(LIST_QUERY_STRING);
128             //.submitQuery(LIST_QUERY_STRING);
129             assert doc != null;
130             NodeList nl = doc.getElementsByTagNameNS("*", "Identifier");
131             log.debug("...got doc with "+nl.getLength()+" Identifier elements");
132             List namesList = new ArrayList();
133             for (int i = 0; i < nl.getLength(); i++) {
134                 IdentifierType it;
135                 try {
136                     it = (IdentifierType) Unmarshaller.unmarshal(IdentifierType.class, nl.item(i));
137                     String name = it.getAuthorityID() + "/" + it.getResourceKey();
138                     namesList.add(name);
139                     log.debug("Adding "+name+" to list of applications");
140                 } catch (MarshalException e1) {
141                     log.error("listApplications(): problem unmarshalling " + nl.item(i) + " cannot add to list", e1);
142                 } catch (ValidationException e1) {
143                     log.error("listApplications(): problem validating " + nl.item(i) + " cannot add to list", e1);
144                 }
145             }
146 
147             if (log.isTraceEnabled()) {
148                 log.trace("listApplications() - end - return value = " + namesList);
149             }
150             return namesList;
151         } catch (RegistryException e) {
152             log.error("listApplications Failed with exception from registry", e);
153             throw e;
154         }
155     }
156 
157     /***
158      * Get application by name from the registry
159      * 
160      * @see org.astrogrid.portal.workflow.intf.ApplicationRegistry#getDescriptionFor(java.lang.String)
161      * @param applicationName application name in registry as returned by
162      *            listApplications()
163      * @return a WebHttpApplication containing all the details stored in the
164      *         registry
165      * @throws CastorException castor unmarshalling probs
166      * @throws RegistryCallException on registry comms probs
167      */
168     private CeaHttpApplicationType getDescriptionFor(String applicationName) throws RegistryException, CastorException {
169         if (log.isTraceEnabled()) {
170             log.trace("getDescriptionFor(String applicationName = " + applicationName + ") - start");
171         }
172 
173         try {
174             final Document doc = service.getResourceByIdentifier(applicationName);
175             assert doc != null;
176 
177             final CeaHttpApplicationType webApplication = (CeaHttpApplicationType) unmarshal(doc,
178                     CeaHttpApplicationType.class, "Resource");
179             
180             if (log.isTraceEnabled()) {
181                 log.trace("getDescriptionFor(String) - end - return value = " + webApplication);
182             }
183             return webApplication;
184         } catch (CastorException e) {
185             log.error("getDescriptionFor " + applicationName + " - castor failed to parse result", e);
186             throw e;
187         } catch (RegistryException e) {
188             log.error("getDescriptionFor " + applicationName + " - exception from registry", e);
189             throw e;
190         }
191     }
192     
193 
194     /***
195      * @see org.astrogrid.component.descriptor.ComponentDescriptor#getInstallationTest()
196      */
197     public Test getInstallationTest() {
198         TestSuite suite = new TestSuite();
199         suite.addTest(new InstallationTest("testRegistryEndPointPropertyFound"));
200         suite.addTest(new InstallationTest("testRegistryServiceFound"));
201         suite.addTest(new InstallationTest("testRegistryContainsOneOrMoreHttpApplications"));
202         return suite;
203     }
204     /***
205      * Some installation tests.
206      * There's a certain amount of redundancy here (test 2 will fail if test 1 does etc)
207      * but the tests are performed separately to make problems
208      * clearer to the user.
209      * @author jdt
210      */
211     public class InstallationTest extends TestCase {
212         public InstallationTest(String arg0) {
213             super(arg0);
214         }
215         /***
216          * Can we find the endpoint of the registry in properties?
217          * Not convinced this should be here, but it has
218          * the side effect that it helps us determine if *any*
219          * properties can be found 
220          *
221          */
222         public void testRegistryEndPointPropertyFound() {
223             //@TODO in SNAPSHOT get this from RegistryDelegateFactory
224             //RegistryDelegateFactory.;
225             String key = "org.astrogrid.registry.query.endpoint";
226             try {
227                 String endPoint = SimpleConfig.getProperty(key);
228                 assertNotNull("The Property "+key+" is not set - check your configuration file");
229             } catch (ConfigException e ) {
230                 fail("There was a problem accessing the configuration file: "+e.getMessage());
231             } catch (PropertyNotFoundException e) {
232                 fail("The Property "+key+" is not set - check your configuration file");
233             }
234         }
235         public void testRegistryServiceFound() throws RegistryException {
236             assertNotNull("No registry service found", service);
237             // The registry client seems to return a non-null service
238             // even when it is up.  The following is a hack that should
239             // fail when the registry is down.
240             // @TODO replace with something more elegant when the registry allows
241             Object doc = service.loadRegistry();
242             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);
243         }
244         public void testRegistryContainsOneOrMoreHttpApplications() throws IOException {
245             Collection apps = getHttpApplications();
246             assertFalse("There were no http applications in the registry.   Check your registry entries and this error might go away.", apps.isEmpty());
247         }
248 
249     }
250     /***
251      * Unmarshall a node of a Document into a class
252      * 
253      * @param doc the document
254      * @param clazz the class you hope to get back
255      * @param elementName the element name in the document you hope matches the
256      *            class
257      * @return object that will need casting...
258      * @throws MarshalException
259      * @throws ValidationException
260      * @throws RegistryCallException
261      */
262     private Object unmarshal(final Document doc, final Class clazz, final String elementName) throws MarshalException,
263             ValidationException, RegistryException {
264         if (log.isTraceEnabled()) {
265             log.trace("unmarshal(Document doc = " + doc + ", Class clazz = " + clazz + ", String elementName = "
266                     + elementName + ") - start");
267         }
268 
269         if (log.isDebugEnabled()) {
270         	log.debug("Unmarshalling document:");
271         	log.debug(DomHelper.DocumentToString(doc));
272         	log.debug("=======================");
273         }
274         //      navigate down to the bit we're interested in.
275         final NodeList nls = doc.getElementsByTagNameNS("*", elementName);
276         if (nls.getLength() == 0) {
277             throw new RegistryException("Registry entry has no " + elementName + " Element");
278         }
279         final Element ns = (Element) nls.item(0);
280         ns.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); //bug-fix work around.
281         Object returnObject = Unmarshaller.unmarshal(clazz, ns);
282         if (log.isTraceEnabled()) {
283             log.trace("unmarshal(Document, Class, String) - end - return value = " + returnObject);
284         }
285         return returnObject;
286     }
287 
288     /***
289      * Returns a List of WebHttpApplications from the Registry. Note, that if
290      * this method returns an empty list, it might indicate communication
291      * problems with the registry, or that the registry entries are invalid and
292      * not unmarshalable
293      * 
294      * @throws
295      * @see org.astrogrid.applications.http.registry.RegistryQuerier#getHttpApplications()
296      */
297     public Collection getHttpApplications() throws IOException {
298         if (log.isTraceEnabled()) {
299             log.trace("getHttpApplications() - start");
300         }
301         
302 
303         try {
304             List apps = new ArrayList();
305 
306             List names = listApplications();
307             log.debug("Got a list of "+names.size()+" applications");
308             Iterator it = names.iterator();
309             while (it.hasNext()) {
310                 String name = (String) it.next();
311                 try {
312                     final CeaHttpApplicationType description = getDescriptionFor(name);
313                     ApplicationKindType type=description.getApplicationDefinition().getApplicationKind();
314                     if (ApplicationKindType.HTTP.equals(type)) {
315                         apps.add(description);
316                         log.debug("Adding "+description.getShortName()+" to list");
317                     } else {
318                         log.warn("Description "+description.getShortName()+" is listed in the registry as a CeaHttpApplication, but is actually a "+type+" app.  Must be an http app.");
319                         log.warn("=>not adding to list");
320                     }
321                 } catch (CastorException e) {
322                     log.info("getHttpApplications(): problem unmarshalling " + name + ", not adding to list", e);
323                 }
324             }
325 
326             if (log.isTraceEnabled()) {
327                 log.trace("getHttpApplications() - end - return value = " + apps);
328             }
329             return apps;
330         } catch (RegistryException e) {
331             log.error("getHttpApplications(): RegistryException", e);
332             throw new IOException("Problem communicating with Registry");
333         }
334     }
335 
336     public String toString() {
337         if (log.isTraceEnabled()) {
338             log.trace("toString() - start");
339         }
340 
341         StringBuffer buffer = new StringBuffer();
342         buffer.append("[RegistryQuerierImpl:");
343         buffer.append(" log: ");
344         buffer.append(log);
345         buffer.append(" service: ");
346         buffer.append(service);
347         buffer.append(" LIST_QUERY_STRING: ");
348         buffer.append(LIST_QUERY_STRING);
349         buffer.append("]");
350         String returnString = buffer.toString();
351         if (log.isTraceEnabled()) {
352             log.trace("toString() - end - return value = " + returnString);
353         }
354         return returnString;
355     }
356 
357     /* (non-Javadoc)
358      * @see org.astrogrid.component.descriptor.ComponentDescriptor#getName()
359      */
360     public String getName() {
361         return "RegistryQuerierImpl";
362     }
363 
364     /* (non-Javadoc)
365      * @see org.astrogrid.component.descriptor.ComponentDescriptor#getDescription()
366      */
367     public String getDescription() {
368         return "Talks to the registry to get http applications";
369     }
370 }