QManWsResource.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.management.wsdm.muse.resources;
022 
023 import java.lang.reflect.Method;
024 import java.util.Collection;
025 import java.util.Collections;
026 import java.util.HashMap;
027 import java.util.LinkedHashMap;
028 import java.util.Map;
029 import java.util.Map.Entry;
030 
031 import javax.xml.namespace.QName;
032 
033 import org.apache.muse.core.Capability;
034 import org.apache.muse.core.Environment;
035 import org.apache.muse.core.ResourceManager;
036 import org.apache.muse.core.routing.MessageHandler;
037 import org.apache.muse.util.xml.XmlUtils;
038 import org.apache.muse.ws.addressing.EndpointReference;
039 import org.apache.muse.ws.addressing.WsaConstants;
040 import org.apache.muse.ws.addressing.soap.SoapConstants;
041 import org.apache.muse.ws.addressing.soap.SoapFault;
042 import org.apache.muse.ws.addressing.soap.SoapUtils;
043 import org.apache.muse.ws.resource.WsResource;
044 import org.apache.muse.ws.resource.metadata.MetadataDescriptor;
045 import org.apache.muse.ws.resource.metadata.WsrmdConstants;
046 import org.apache.muse.ws.resource.metadata.impl.SimpleMetadataDescriptor;
047 import org.apache.muse.ws.resource.metadata.impl.WsrmdUtils;
048 import org.apache.muse.ws.resource.properties.ResourcePropertyCollection;
049 import org.apache.muse.ws.resource.properties.impl.SimpleResourcePropertyCollection;
050 import org.apache.muse.ws.resource.properties.impl.WsrpUtils;
051 import org.apache.muse.ws.resource.properties.schema.ResourcePropertiesSchema;
052 import org.apache.muse.ws.resource.properties.schema.impl.SimpleResourcePropertiesSchema;
053 import org.apache.muse.ws.wsdl.WsdlUtils;
054 import org.apache.qpid.management.Messages;
055 import org.apache.qpid.management.wsdm.common.ThreadSessionManager;
056 import org.apache.qpid.transport.util.Logger;
057 import org.w3c.dom.Document;
058 import org.w3c.dom.Element;
059 
060 /**
061  * QMan WS resource.
062  * We could say that this is a QMan manageable entity under the 
063  * WS-DM perspective.
064  
065  @author Andrea Gazzarini
066  */
067 @SuppressWarnings("unchecked")
068 public class QManWsResource implements WsResource
069 {    
070   private final static Logger LOGGER = Logger.get(QManWsResource.class);
071     
072   /**
073    * Internal state of this resource.
074    
075    @author Andrea Gazzarini
076    */
077   interface State 
078   {
079     /**
080      * Provides initialization of this resource.
081      
082      @throws SoapFault when the initialization fails.
083      */
084     void initialize() throws SoapFault;
085     
086     /**
087      * Returns true if this resource has been initialized.
088      
089      @return true if this resource has been initialized.
090      */
091     boolean hasBeenInitialized();
092     
093     /**
094      * Returns true if this resource has been shutdown.
095      
096      @return true if this resource has been shutdown.
097      */
098     boolean hasBeenShutdown();
099     
100     /**
101      * Shuts down this resource.
102      
103      @throws SoapFault when the shutdown procedure fails.
104      */
105     void shutdown() throws SoapFault;    
106   }
107   
108   private final State _hasBeenShutdown = new State()
109   {
110     /**
111      * Return false because this resource has been shutdown so therefore 
112      * initialization occurred.
113      
114      @return true;
115      */
116     public boolean hasBeenInitialized()
117     {
118       return true;
119     }
120 
121     /**
122      * Returns true because this state indicates that resource has been shutdown.
123      
124      @return true.
125      */
126     public boolean hasBeenShutdown()
127     {
128       return true;
129     }
130 
131     /**
132      * Since this resource has been shutdown the initialization
133      * cannot be performed again.
134      * As conseguence of that this method throws an exception.
135      
136      @throws SoapFault each time this method is called.
137      */
138     public void initialize() throws SoapFault
139     {
140       LOGGER.error(Messages.QMAN_100031_WS_RESOURCE_ALREADY_INITIALIZED);
141       throw new SoapFault(Messages.QMAN_100031_WS_RESOURCE_ALREADY_INITIALIZED);
142     }
143 
144     public void shutdown() throws SoapFault
145     {
146       LOGGER.error(Messages.QMAN_100033_WS_RESOURCE_ALREADY_SHUTDOWN);
147       throw new SoapFault(Messages.QMAN_100033_WS_RESOURCE_ALREADY_SHUTDOWN);      
148     }
149   };
150   
151   private final State _hasBeenInitialized = new State()
152   {
153     /**
154      * Returns true because this is the state where a resource is when it 
155      * has been initialized.
156      
157      @return true.
158      */
159     public boolean hasBeenInitialized()
160     {
161       return true;
162     }
163 
164     /**
165      * Returns false because this resource has been initialized but no shutdown request
166      * has been received.
167      
168      @return false.
169      */
170     public boolean hasBeenShutdown()
171     {
172       return false;
173     }
174 
175     /**
176      * A resource in this state cannot be initialized again so if this method is called an
177      * exception is thrown.
178      
179      @throws SoapFault each time this method is called.
180      */
181     public void initialize() throws SoapFault
182     {
183       LOGGER.error(Messages.QMAN_100031_WS_RESOURCE_ALREADY_INITIALIZED);
184       throw new SoapFault(Messages.QMAN_100031_WS_RESOURCE_ALREADY_INITIALIZED);
185     }
186     
187     /**
188      * Shuts down this resource.
189      
190      @throws SoapFault when the shutdown procedure fails.
191      */
192     public void shutdown() throws SoapFault 
193     {
194           shutdownCapabilities();
195             
196           ResourceManager manager = getResourceManager();
197           
198           if (manager.getResource(_enpointReference!= null)
199           {
200               manager.removeResource(_enpointReference);
201           }
202           
203           _currentState = _hasBeenShutdown;
204     }    
205   };
206   
207   /**
208    * The initial state of this resource.
209    * As the name suggests, it is not yet initialized.
210    */
211   private final State _notYetInitialized = new State() 
212   {
213     /**
214      * Provides initialization of this resource.
215      
216      @throws SoapFault when the initialization fails.
217      */
218     public void initialize() throws SoapFault
219     {
220           _properties = new SimpleResourcePropertyCollection();
221           _wsdl = ThreadSessionManager.getInstance().getSession().getWsdlDocument();
222           
223           ResourcePropertiesSchema schema = createPropertiesSchema(_wsdl);
224           _properties.setSchema(schema);
225         
226           MetadataDescriptor metadata = createMetadataDescriptor(_wsdl);
227           _properties.setMetadata(metadata);
228                   
229           initializeCapabilities();
230                     
231           _properties.applyMetadata();
232           
233           // Resource intialization completed : Let's make a state change.
234           _currentState = _hasBeenInitialized;
235     }
236 
237     /**
238      * Shuts down this resource.
239      
240      @throws SoapFault when the shutdown procedure fails.     */
241     public void shutdown() throws SoapFault
242     {
243       LOGGER.error(Messages.QMAN_100032_WS_RESOURCE_NOT_YET_INITIALIZED);
244       throw new SoapFault(Messages.QMAN_100032_WS_RESOURCE_NOT_YET_INITIALIZED);      
245     }
246     
247     /**
248      * Returns false because this state indicates that 
249      * the resource has not yet been initialized.
250      
251      @return false;
252      */
253     public boolean hasBeenInitialized()
254     {
255       return false;
256     }
257 
258     /**
259      * Returns false because the resource, when is in this state 
260      * hasn't been initialized and as conseguence of that hasn't 
261      * been shutdonm.
262      
263      @return false;
264      */
265     public boolean hasBeenShutdown()
266     {
267       return false;
268     }
269   };
270   
271     private Map<String,Capability>  _capabilitiesByAction = new HashMap<String, Capability>();
272     private Map<String, Capability> _capabilitiesByURI = new LinkedHashMap<String, Capability>();
273     
274     private String _contextPath;
275     private Environment _environment;    
276     private EndpointReference _enpointReference;
277     
278     private State _currentState = _notYetInitialized;
279     
280     private ResourceManager _resourceManager;
281     private ResourcePropertyCollection _properties;
282 
283     private Map<String,String> _initParameters = Collections.EMPTY_MAP;
284     
285     // Workaround : muse is using and hardcoded java.util.logging.Logger but we should use 
286     // SLF4j so this is the original implementatation that won't never be used (on QMan classes)
287     private java.util.logging.Logger _logger;
288     
289     private Document _wsdl;    
290     private String _wsdlPath;
291     private QName _wsdlPortType;
292     
293     /**
294      * Adds the given capability to this resource.
295      
296      @param capability the capability to be added.
297      */
298     public void addCapability(Capability capability)
299     {
300         capability.setResource(this);
301         capability.setLog(getLog());
302         capability.setEnvironment(getEnvironment());
303         
304         String uri = capability.getCapabilityURI();
305         _capabilitiesByURI.put(uri, capability);
306         
307         LOGGER.debug(
308             Messages.QMAN_200033_CAPABILITY_CLASS_HAS_BEEN_ADDED, 
309             capability.getClass(),
310             uri);
311     }
312     
313     /**
314      * Returns the capability associated with the given URI.
315      
316      @return the capability associated with the given URI.
317      */
318     public Capability getCapability(String capabilityURI)
319     {
320         return _capabilitiesByURI.get(capabilityURI);
321     }
322     
323     /**
324      * Returns a collection with all registered capability URIs.
325      
326      @return a collection with all registered capability URIs.
327      */
328     public final Collection getCapabilityURIs()
329     {
330         return Collections.unmodifiableSet(_capabilitiesByURI.keySet());
331     }
332     
333     /**
334      * Returns the context path of this resource.
335      
336      @return the context path of this resource.
337      */
338     public final String getContextPath()
339     {
340         return _contextPath;
341     }
342 
343     /**
344      * Returns the endpoint reference of this resource.
345      
346      @return the endpoint reference of this resource.
347      */
348     public EndpointReference getEndpointReference()
349     {
350         return _enpointReference;
351     }
352     
353     /**
354      * Returns the enviroment associated with this resource.
355      
356      @return the enviroment associated with this resource.
357      */
358     public final Environment getEnvironment()
359     {
360         return _environment;
361     }
362     
363     /**
364      * Returns the initialization parameter of this resource associated with 
365      * the given name.
366      
367      @param name the init parameter name.
368      @return the initialization parameter associated with the given name.
369      */
370     public final String getInitializationParameter(String name)
371     {
372         return (String)getInitializationParameters().get(name);
373     }
374     
375     /**
376      * Returns the map containing all init parameters of this resource.
377      
378      @return the map containing all init parameters of this resource.
379      */
380     public final Map<String,String> getInitializationParameters()
381     {
382         return _initParameters;
383     }
384     
385     /**
386      * N.A. This resource uses QMan logging instead of plain java.util.logger 
387      * implementation.
388      */
389     public final java.util.logging.Logger getLog()
390     {
391         return _logger;
392     }
393     
394     /**
395      * Returns the resource manager associated with this resource.
396      
397      @return the resource manager associated with this resource.
398      */
399     public ResourceManager getResourceManager()
400     {
401         return _resourceManager;
402     }
403     
404     /**
405      * Returns the wsdl (relative) path of this resource.
406      
407      @return the wsdl (relative) path of this resource.
408      */
409     public String getWsdlPath()
410     {
411         return _wsdlPath;
412     }
413     
414     /**
415      * Returns the port type of this resource.
416      
417      @return the port type of this resource.
418      */
419     public final QName getWsdlPortType()
420     {
421         return _wsdlPortType;
422     }
423     
424     /**
425      * Returns true if this resource has been initialized, false otherwise.
426      
427      @return true if this resource has been initialized, false otherwise.
428      */
429     public final boolean hasBeenInitialized()
430     {
431         return _currentState.hasBeenInitialized();
432     }
433     
434     /**
435      * Returns true if this resource has been shutdown, false otherwise.
436      
437      @return true if this resource has been shutdown, false otherwise.
438      */
439     public final boolean hasBeenShutdown()
440     {
441         return _currentState.hasBeenShutdown();
442     }
443     
444     /**
445      * Checks if a capability with the given URI is available for this resource.
446      
447      @return true if a capability with the given URI is available for this resource, false otherwise.
448      */
449     public final boolean hasCapability(String capabilityURI)
450     {
451         return getCapability(capabilityURI!= null;
452     }
453     
454     /**
455      * Returns the collection containing all properties of this resource.
456      
457      @return the collection containing all properties of this resource.
458      */
459     public final ResourcePropertyCollection getPropertyCollection()
460     {
461         return _properties;
462     }    
463     
464     /**
465      * Return the WSDL document of this resource.
466      
467      @return the WSDL document of this resource.
468      */
469     public Document getWsdl()
470     {
471       return _wsdl;
472     }
473     
474     /**
475      * Initializes this resources.
476      * Note that the what needs to be done depends on the current state of this
477      * resource.
478      
479      @throws SoapFault when the initialization fails.
480      */
481     public void initialize() throws SoapFault
482     {      
483       _currentState.initialize();
484     }
485         
486     /**
487      * Invokes the action specified in the given soap request on this resource.
488      
489      @param requestBody the SOAP body.
490      @return the result of the invocation as org.w3c.dom.Element
491      */
492     public Element invoke(Element requestBody)
493     {
494         String action =  _environment.getAddressingContext().getAction();  
495         Capability capability = getCapabilityForAction(action);
496         
497         // Sanity check : is there a capability for the given action?
498         if (capability == null)
499         {
500             SoapFault wsaFault = new SoapFault(
501                 String.format(
502                     Messages.ACTION_NOT_SUPPORTED, 
503                     action,getContextPath()));
504 
505             wsaFault.setCode(SoapConstants.SENDER_QNAME);
506             wsaFault.setSubCode(WsaConstants.ACTION_NOT_SUPPORTED_FAULT_QNAME);
507             
508             Element detail = XmlUtils.createElement(WsaConstants.PROBLEM_ACTION_QNAME);
509             XmlUtils.setElement(detail, WsaConstants.ACTION_QNAME, action);
510             wsaFault.setDetail(detail);
511             
512             LOGGER.error(
513                 Messages.QMAN_100020_ACTION_NOT_SUPPORTED, 
514                 action,
515                 getContextPath());
516 
517             return wsaFault.toXML();
518         }
519         
520         MessageHandler handler = capability.getMessageHandler(action);
521         Method method = handler.getMethod();
522         
523         try
524         {
525           Object[]parameters = handler.fromXML(requestBody);
526             Object result = method.invoke(capability, parameters);
527             return handler.toXML(result)
528         }
529         catch (Throwable throwable)
530         {
531           LOGGER.error(
532               throwable, 
533               Messages.QMAN_100037_INVOKE_OPERATION_FAILURE);
534           
535             SoapFault response = SoapUtils.convertToFault(
536                 (throwable.getCause()!= null
537                   ? throwable.getCause()
538                   : throwable);
539             return response.toXML();
540         }
541     }
542     
543     /**
544      * Sets the context path of this resource.
545      
546      @param contextPath the context path of this resource.
547      */
548     public final void setContextPath(String contextPath)
549     {
550         _contextPath = contextPath;
551     }
552     
553     /**
554      * Sets the endpoint reference of this resource.
555      
556      @param endpointReference the endpoint reference of this resource.
557      */
558     public final void setEndpointReference(EndpointReference endpointReference)
559     {
560         if (_enpointReference != null && hasBeenInitialized())
561             throw new RuntimeException(("ExistingResourceEPR"));
562         
563         _enpointReference = endpointReference;
564     }
565     
566     /**
567      * Sets the context environment of this resource.
568      
569      @param environment the context environment of this resource.
570      */
571     public final void setEnvironment(Environment environment)
572     {
573         _environment = environment;
574     }
575     
576     /**
577      * Sets the initialization parameters of this resource.
578      
579      @param parameters the init parameters of this resource.
580      */
581     public final void setInitializationParameters(Map parameters)
582     {
583         _initParameters = (parameters != null
584           ? parameters 
585           : Collections.EMPTY_MAP;
586     }
587     
588     /**
589      * N.A. for this resource. QMan logging mechanism is used for that.
590      */
591     public final void setLog(java.util.logging.Logger log)
592     {
593         _logger = log;
594     }
595     
596     /**
597      * Sets the resource manager owner of this resource.
598      
599      @param manager the resource manager of this resource.
600      */
601     public void setResourceManager(ResourceManager manager)
602     {
603         _resourceManager = manager;
604     }
605     
606     /**
607      * Sets the WSDL (relative) path of this resource.
608      
609      @param wsdlPath the WSDL (relative) path of this resource.
610      */
611     public final void setWsdlPath(String wsdlPath)
612     {
613       this._wsdlPath = wsdlPath;
614     }
615     
616     /**
617      * Sets the port type of this resource.
618      
619      @param wsdlPortType the port type of this resource.
620      */
621     public final void setWsdlPortType(QName wsdlPortType)
622     {
623         _wsdlPortType = wsdlPortType;
624     }
625         
626     /**
627      * Shutdown procedure for this resource.
628      
629      @throws SoapFault when the shutdown procedure fails.
630      */
631     public synchronized void shutdown() throws SoapFault
632     {
633       _currentState.shutdown();
634     }
635         
636     /**
637      * Returns a string representation of this resource.
638      * Basically the resource endpoint reference (as a string) is returned.
639      
640      @return the resource endpoint reference (as a string) is returned.
641      */
642     public String toString()
643     {
644         return getEndpointReference().toString();
645     }     
646     
647     /**
648      * Initializes capabilities of this resource.
649      
650      @throws SoapFault when at least one capability fails to initialize.
651      */
652     private void initializeCapabilities() throws SoapFault
653     {
654         for (Entry<String, Capability> entry : _capabilitiesByURI.entrySet()) 
655         {
656           Capability capability = entry.getValue();
657       capability.initialize();
658       
659       for (Object action : capability.getActions()) 
660       {
661                 _capabilitiesByAction.put((String)action, capability);
662       }
663       
664       capability.initializeCompleted();
665     }
666     }
667     
668     /**
669      * Shutdown procedure for all registered capabilities of this resource.
670      
671      @throws SoapFault when at least one capability shutdown fails.
672      */
673     private void shutdownCapabilities() throws SoapFault
674     {
675         for (Entry<String,Capability> entry : _capabilitiesByURI.entrySet()) 
676         {
677           Capability capabilty = entry.getValue();
678           capabilty.prepareShutdown();
679           capabilty.shutdown();
680     }        
681     }    
682     
683     /**
684      * Creates a metadata descriptor for this resource.
685      
686      @param wsdl the WSDL document. 
687      @return a metadata descriptor for this resource.
688      @throws SoapFault when it's not possible build the descriptor.
689      */
690     private MetadataDescriptor createMetadataDescriptor(Document wsdlthrows SoapFault
691     {
692         try 
693         {      
694           Element portTypeXML = WsdlUtils.getPortType(wsdl, getWsdlPortType());
695           
696           String rmdName = XmlUtils.getAttribute(
697               portTypeXML, 
698               WsrmdConstants.DESCRIPTOR_ATTR_QNAME);
699           
700           String rmdPath = XmlUtils.getAttribute(
701               portTypeXML, 
702               WsrmdConstants.DESCRIPTOR_LOCATION_ATTR_QNAME);
703           
704           LOGGER.debug(Messages.QMAN_200034_RMD_NAME, rmdName);
705           LOGGER.debug(Messages.QMAN_200035_RMD_PATH, rmdPath);
706           
707           Environment env = getEnvironment();
708           String path = env.createRelativePath(getWsdlPath(), rmdPath);        
709           Document rmdDoc = env.getDocument(path);
710           
711           Element[] additionalProperties = 
712             ThreadSessionManager
713               .getInstance()
714               .getSession()
715               .getResourceMetadataDescriptor();
716           
717           Element metadataDescriptor = WsrmdUtils.getMetadataDescriptor(rmdDoc, rmdName);
718         
719           for (Element element : additionalProperties
720           {
721             
722 //            rmdDoc.importNode(element, true);
723         Element adopted = (ElementrmdDoc.importNode(element,false);
724         metadataDescriptor.appendChild(adopted);
725       }
726       
727       return new SimpleMetadataDescriptor(metadataDescriptor);
728         
729         catch(Exception exception)
730         {
731           LOGGER.error(
732               exception,
733               Messages.QMAN_100021_RMD_BUID_FAILURE,
734               getContextPath());
735           throw new SoapFault(exception);
736         }
737     }    
738     
739     /**
740      * Returns the capability associated with the given action.
741      
742      @param action the wsa:action of the requested capability.
743      @return the capability associated with the given action.
744      */
745     private Capability getCapabilityForAction(String action)
746     {
747         return (Capability)_capabilitiesByAction.get(action);
748     }    
749     
750     /**
751      * Creates a WSRP document representing schema properties for this resource.
752      
753      @param wsdl the DOM document holding the resource's WSDL.       
754      @return the WSRP document schema.
755      */
756     private ResourcePropertiesSchema createPropertiesSchema(Document wsdl)
757     {
758         QName wsrpName = WsrpUtils.getPropertiesName(wsdl, getWsdlPortType());
759         Element wsrpDoc = WsdlUtils.getElementDeclaration(wsdl, wsrpName);        
760         return new SimpleResourcePropertiesSchema(wsrpName, wsrpDoc);
761     }        
762 }