OW_CIMXMLListener.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005 Novell, Inc. All rights reserved.
00003 * Copyright (C) 2001-2004 Vintela, Inc. All rights reserved.
00004 *
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 *
00008 *  - Redistributions of source code must retain the above copyright notice,
00009 *    this list of conditions and the following disclaimer.
00010 *
00011 *  - Redistributions in binary form must reproduce the above copyright notice,
00012 *    this list of conditions and the following disclaimer in the documentation
00013 *    and/or other materials provided with the distribution.
00014 *
00015 *  - Neither the name of Vintela, Inc., Novell, Inc., nor the names of its
00016 *    contributors may be used to endorse or promote products derived from this
00017 *    software without specific prior written permission.
00018 *
00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc., Novell, Inc., OR THE CONTRIBUTORS
00023 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00024 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00025 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00026 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00027 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029 * POSSIBILITY OF SUCH DAMAGE.
00030 *******************************************************************************/
00031 
00037 #include "OW_config.h"
00038 #include "OW_CIMXMLListener.hpp"
00039 #include "OW_CIMListenerCallback.hpp"
00040 #include "OW_HTTPServer.hpp"
00041 #include "OW_XMLListener.hpp"
00042 #include "OW_HTTPException.hpp"
00043 #include "OW_Format.hpp"
00044 #include "OW_MutexLock.hpp"
00045 #include "OW_ListenerAuthenticator.hpp"
00046 #include "OW_CIMInstance.hpp"
00047 #include "OW_CIMValue.hpp"
00048 #include "OW_CIMClass.hpp"
00049 #include "OW_CIMProperty.hpp"
00050 #include "OW_RandomNumber.hpp"
00051 #include "OW_ServiceEnvironmentIFC.hpp"
00052 #include "OW_ConfigOpts.hpp"
00053 #include "OW_ServerSocket.hpp"
00054 #include "OW_SelectEngine.hpp"
00055 #include "OW_SelectableIFC.hpp"
00056 #include "OW_IOException.hpp"
00057 #include "OW_Thread.hpp"
00058 #include "OW_Assertion.hpp"
00059 #include "OW_ClientCIMOMHandle.hpp"
00060 #include "OW_CIMProtocolIFC.hpp"
00061 #include "OW_NullLogger.hpp"
00062 #include "OW_FileSystem.hpp"
00063 #include "OW_ConfigFile.hpp"
00064 
00065 #include <algorithm> // for std::remove
00066 
00067 namespace OW_NAMESPACE
00068 {
00069 
00070 using namespace WBEMFlags;
00071 
00072 // This anonymous namespace has the effect of giving this class internal
00073 // linkage.  That means it won't cause a link error if another translation
00074 // unit has a class with the same name.
00075 namespace
00076 {
00077 
00078 const String COMPONENT_NAME("ow.listener.cimxml");
00079 
00080 #ifdef OW_WIN32
00081 class EventSelectable : public SelectableIFC
00082 {
00083 public:
00084    EventSelectable()
00085       : SelectableIFC()
00086       , m_event(NULL)
00087    {
00088       if(!(m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL)))
00089       {
00090          OW_THROW(IOException, "Unable to create WIN32 event handle");
00091       }
00092    }
00093    ~EventSelectable()
00094    {
00095       if(m_event)
00096       {
00097          ::CloseHandle(m_event);
00098       }
00099    }
00100    Select_t getSelectObj() const
00101    {
00102       Select_t st;
00103       st.event = m_event;
00104       return st;
00105    }
00106 
00107    void setEvent()
00108    {
00109       if(m_event)
00110       {
00111          if(!::SetEvent(m_event))
00112          {
00113             OW_THROW(IOException, "Signaling termination event failed");
00114          }
00115       }
00116    }
00117 
00118    void resetEvent()
00119    {
00120       if(m_event)
00121       {
00122          ::ResetEvent(m_event);
00123       }
00124    }
00125 
00126 private:
00127    HANDLE m_event;
00128 };
00129 typedef IntrusiveReference<EventSelectable> EventSelectableRef;
00130 
00131 #endif
00132 
00133 typedef std::pair<SelectableIFCRef, SelectableCallbackIFCRef> SelectablePair_t;
00134 class CIMXMLListenerServiceEnvironment : public ServiceEnvironmentIFC
00135 {
00136 public:
00137    CIMXMLListenerServiceEnvironment(
00138       const ConfigFile::ConfigMap& configItems, 
00139       const AuthenticatorIFCRef& authenticator,
00140       RequestHandlerIFCRef listener,
00141       const LoggerRef& logger,
00142       Reference<Array<SelectablePair_t> > selectables)
00143    : m_configItems(configItems)
00144    , m_authenticator(authenticator)
00145    , m_XMLListener(listener)
00146    , m_logger(logger ? logger : LoggerRef(new NullLogger))
00147    , m_selectables(selectables)
00148    {
00149       setConfigItem(ConfigOpts::HTTP_SERVER_MAX_CONNECTIONS_opt, String(10), E_PRESERVE_PREVIOUS);
00150       setConfigItem(ConfigOpts::HTTP_SERVER_SINGLE_THREAD_opt, "false", E_PRESERVE_PREVIOUS);
00151       setConfigItem(ConfigOpts::HTTP_SERVER_ENABLE_DEFLATE_opt, "true", E_PRESERVE_PREVIOUS);
00152       setConfigItem(ConfigOpts::HTTP_SERVER_USE_DIGEST_opt, "false", E_PRESERVE_PREVIOUS);
00153       setConfigItem(ConfigOpts::HTTP_SERVER_USE_UDS_opt, "false", E_PRESERVE_PREVIOUS);
00154 
00155       //setConfigItem(ConfigOpts::ALLOW_ANONYMOUS_opt, "true", E_PRESERVE_PREVIOUS);
00156       setConfigItem(ConfigOpts::HTTP_SERVER_ENABLE_DEFLATE_opt, "false", E_PRESERVE_PREVIOUS);
00157    }
00158    virtual ~CIMXMLListenerServiceEnvironment() {}
00159    virtual bool authenticate(String &userName,
00160       const String &info, String &details, OperationContext& context) const
00161    {
00162       // TODO what if m_authenticator == 0?  or should this even be allowed...
00163       if (!m_authenticator)
00164       {
00165          return false; 
00166       }
00167       return m_authenticator->authenticate(userName, info, details, context);
00168    }
00169    virtual void addSelectable(const SelectableIFCRef& obj,
00170       const SelectableCallbackIFCRef& cb)
00171    {
00172       m_selectables->push_back(std::make_pair(obj, cb));
00173    }
00174 
00175    struct selectableFinder
00176    {
00177       selectableFinder(const SelectableIFCRef& obj) : m_obj(obj) {}
00178       template <typename T>
00179       bool operator()(const T& x)
00180       {
00181          return x.first == m_obj;
00182       }
00183       const SelectableIFCRef& m_obj;
00184    };
00185 
00186    virtual void removeSelectable(const SelectableIFCRef& obj)
00187    {
00188       m_selectables->erase(std::remove_if (m_selectables->begin(), m_selectables->end(),
00189          selectableFinder(obj)), m_selectables->end());
00190    }
00191    virtual String getConfigItem(const String &name, const String& defRetVal) const
00192    {
00193       return ConfigFile::getConfigItem(m_configItems, name, defRetVal);
00194    }
00195    virtual StringArray getMultiConfigItem(const String &itemName, 
00196       const StringArray& defRetVal, const char* tokenizeSeparator) const
00197    {
00198       return ConfigFile::getMultiConfigItem(m_configItems, itemName, defRetVal, tokenizeSeparator);
00199    }
00200    virtual void setConfigItem(const String& item, const String& value, EOverwritePreviousFlag overwritePrevious)
00201    {
00202       ConfigFile::setConfigItem(m_configItems, item, value, 
00203          overwritePrevious == E_OVERWRITE_PREVIOUS ? ConfigFile::E_OVERWRITE_PREVIOUS : ConfigFile::E_PRESERVE_PREVIOUS);
00204    }
00205    
00206    virtual RequestHandlerIFCRef getRequestHandler(const String&) const
00207    {
00208       RequestHandlerIFCRef ref(m_XMLListener.getLibRef(),
00209             m_XMLListener->clone());
00210       ref->setEnvironment(ServiceEnvironmentIFCRef(const_cast<CIMXMLListenerServiceEnvironment*>(this)));
00211       return ref;
00212    }
00213    virtual LoggerRef getLogger() const
00214    {
00215       return getLogger(COMPONENT_NAME);
00216    }
00217    virtual LoggerRef getLogger(const String& componentName) const
00218    {
00219       LoggerRef rv(m_logger->clone());
00220       rv->setDefaultComponent(componentName);
00221       return rv;
00222    }
00223 private:
00224    ConfigFile::ConfigMap m_configItems;
00225    AuthenticatorIFCRef m_authenticator;
00226    RequestHandlerIFCRef m_XMLListener;
00227    LoggerRef m_logger;
00228    Reference<Array<SelectablePair_t> > m_selectables;
00229 };
00230 class SelectEngineThread : public Thread
00231 {
00232 public:
00233    SelectEngineThread(const Reference<Array<SelectablePair_t> >& selectables)
00234    : Thread()
00235    , m_selectables(selectables)
00236 #ifdef OW_WIN32
00237    , m_stopObject(new EventSelectable)
00238    {
00239    }
00240 #else
00241    , m_stopObject(UnnamedPipe::createUnnamedPipe())
00242    {
00243       m_stopObject->setBlocking(UnnamedPipe::E_NONBLOCKING);
00244    }
00245 #endif
00246 
00250    virtual Int32 run()
00251    {
00252       SelectEngine engine;
00253       SelectableCallbackIFCRef cb(new SelectEngineStopper(engine));
00254       m_selectables->push_back(std::make_pair(m_stopObject, cb));
00255       for (size_t i = 0; i < m_selectables->size(); ++i)
00256       {
00257          engine.addSelectableObject((*m_selectables)[i].first,
00258             (*m_selectables)[i].second);
00259       }
00260       engine.go();
00261       return 0;
00262    }
00263    virtual void doCooperativeCancel()
00264    {
00265 #ifdef OW_WIN32
00266       // signal the event to stop the select engine so the
00267       // thread will exit
00268       m_stopObject->setEvent();
00269 #else
00270       // write something into the stop pipe to stop the select engine so the
00271       // thread will exit
00272       if (m_stopObject->writeInt(0) == -1)
00273       {
00274          OW_THROW_ERRNO_MSG(IOException, "Writing to the termination pipe failed");
00275       }
00276 #endif
00277    }
00278 
00279 private:
00280    Reference<Array<SelectablePair_t> > m_selectables;
00281 #ifdef OW_WIN32
00282    EventSelectableRef m_stopObject;
00283 #else
00284    UnnamedPipeRef m_stopObject;
00285 #endif
00286 };
00287 } // end anonymous namespace
00289 CIMXMLListener::CIMXMLListener(const ConfigFile::ConfigMap& configItems, 
00290                         const CIMListenerCallbackRef& callback, 
00291                         const AuthenticatorIFCRef& authenticator, 
00292                         const LoggerRef& logger)
00293    : m_XMLListener(SharedLibraryRef(0), new XMLListener(callback)) 
00294    //, m_pLAuthenticator(new ListenerAuthenticator)
00295    , m_httpServer(new HTTPServer)
00296    , m_httpListenPort(0)
00297    , m_httpsListenPort(0)
00298 {
00299    Reference<Array<SelectablePair_t> >
00300       selectables(new Array<SelectablePair_t>);
00301    ServiceEnvironmentIFCRef env(new CIMXMLListenerServiceEnvironment(
00302       configItems, 
00303       authenticator, m_XMLListener, logger, selectables)); 
00304    m_httpServer->init(env);
00305    m_httpServer->start();  // The http server will add it's server
00306    // sockets to the environment's selectables, which is really
00307    // the selectabls defined above.  We'll give these to the select engine thread
00308    // below which will use them to run the select engine.
00309    m_httpListenPort = m_httpServer->getLocalHTTPAddress().getPort();
00310    m_httpsListenPort = m_httpServer->getLocalHTTPSAddress().getPort();
00311    // start a thread to run the http server
00312    m_httpThread = new SelectEngineThread(selectables);
00313    m_httpThread->start();
00314 }
00316 CIMXMLListener::~CIMXMLListener()
00317 {
00318    try
00319    {
00320       shutdownHttpServer();
00321       // unregister all the callbacks from the CIMOMs
00322       /*
00323       MutexLock lock(m_mutex);
00324       for (callbackMap_t::iterator i = m_callbacks.begin();
00325          i != m_callbacks.end(); ++i)
00326       {
00327          registrationInfo reg = i->second;
00328    
00329          try
00330          {
00331             deleteRegistrationObjects(reg);
00332          }
00333          catch (Exception&)
00334          {
00335             // if an error occured, then just ignore it.  We don't have any way
00336             // of logging it!
00337          }
00338          catch (...)
00339          {
00340             // who knows what happened, but we need to continue deregistering...
00341          }
00342       }
00343       m_pLAuthenticator = 0;
00344       */
00345    }
00346    catch (...)
00347    {
00348       // don't let exceptions escape
00349    }
00350 }
00351 void
00352 CIMXMLListener::shutdownHttpServer()
00353 {
00354    if (m_httpThread)
00355    {
00356       m_httpThread->cooperativeCancel();
00357       // wait for the thread to quit
00358       m_httpThread->join();
00359       m_httpThread = 0;
00360    }
00361    if (m_httpServer)
00362    {
00363       // stop the http server
00364       m_httpServer->shutdown();
00365       m_httpServer = 0;
00366    }
00367 }
00369 /*
00370 String
00371 CIMXMLListener::registerForIndication(
00372    const String& url,
00373    const String& ns,
00374    const String& filter,
00375    const String& querylanguage,
00376    const String& sourceNamespace,
00377    const CIMListenerCallbackRef& cb,
00378    const ClientAuthCBIFCRef& authCb)
00379 {
00380    registrationInfo reg;
00381    // create an http client with the url from the object path
00382    URL curl(url);
00383    reg.cimomUrl = curl;
00384    ClientCIMOMHandleRef hdl = ClientCIMOMHandle::createFromURL(url, authCb);
00385    String ipAddress = hdl->getWBEMProtocolHandler()->getLocalAddress().getAddress();
00386    CIMClass delivery(CIMNULL);
00387    String urlPrefix;
00388 
00389    UInt16 listenerPort = m_httpsListenPort;
00390    if(!m_certFileName.empty())   // Listener will be recieving over https
00391    {
00392       urlPrefix = "https://";
00393       try
00394       {
00395          delivery = hdl->getClass(ns, "CIM_IndicationHandlerCIMXML");
00396       }
00397       catch (CIMException& e)
00398       {
00399          if (e.getErrNo() == CIMException::NOT_FOUND)
00400          {
00401             // the > 2.6 doesn't exist, try to get the 2.5 class
00402             delivery = hdl->getClass(ns, "CIM_IndicationHandlerXMLHTTPS");
00403          }
00404          else
00405             throw;
00406       }
00407    }
00408    else
00409    {
00410       try
00411       {
00412          delivery = hdl->getClass(ns, "CIM_IndicationHandlerCIMXML");
00413       }
00414       catch (CIMException& e)
00415       {
00416          if (e.getErrNo() == CIMException::NOT_FOUND)
00417          {
00418             // the > 2.6 doesn't exist, try to get the 2.5 class
00419             delivery = hdl->getClass(ns, "CIM_IndicationHandlerXMLHTTP");
00420          }
00421          else
00422             throw;
00423       }
00424       urlPrefix = "http://";
00425       listenerPort = m_httpListenPort;
00426    }
00427    CIMInstance ci = delivery.newInstance();
00428    MutexLock lock(m_mutex);
00429    String httpPath;
00430    RandomNumber rn(0, 0x7FFFFFFF);
00431    do
00432    {
00433       String randomHashValue(rn.getNextNumber());
00434       httpPath = "/cimListener" + randomHashValue;
00435    } while (m_callbacks.find(httpPath) != m_callbacks.end());
00436    reg.httpCredentials = m_pLAuthenticator->getNewCredentials();
00437    ci.setProperty("Destination", CIMValue(urlPrefix + reg.httpCredentials + "@" +
00438             ipAddress + ":" + String(UInt32(listenerPort)) + httpPath));
00439    ci.setProperty("SystemCreationClassName", CIMValue("CIM_System"));
00440    ci.setProperty("SystemName", CIMValue(ipAddress));
00441    ci.setProperty("CreationClassName", CIMValue(delivery.getName()));
00442    ci.setProperty("Name", CIMValue(httpPath));
00443    ci.setProperty("Owner", CIMValue("HTTPXMLCIMListener on " + ipAddress));
00444    try
00445    {
00446       reg.handler = hdl->createInstance(ns, ci);
00447    }
00448    catch (CIMException& e)
00449    {
00450       // We don't care if it already exists, but err out on anything else
00451       if (e.getErrNo() != CIMException::ALREADY_EXISTS)
00452       {
00453          throw;
00454       }
00455       else
00456       {
00457          reg.handler = CIMObjectPath(ns, ci);
00458       }
00459    }
00460    // get class of CIM_IndicationFilter and new instance of it
00461    CIMClass cimFilter = hdl->getClass(ns, "CIM_IndicationFilter", E_LOCAL_ONLY);
00462    ci = cimFilter.newInstance();
00463    // set Query property to query that was passed into function
00464    ci.setProperty("Query", CIMValue(filter));
00465    // set QueryLanguage property
00466    ci.setProperty("QueryLanguage", CIMValue(querylanguage));
00467    ci.setProperty("SystemCreationClassName", CIMValue("CIM_System"));
00468    ci.setProperty("SystemName", CIMValue(ipAddress));
00469    ci.setProperty("CreationClassName", CIMValue(cimFilter.getName()));
00470    ci.setProperty("Name", CIMValue(httpPath));
00471    if (!sourceNamespace.empty())
00472    {
00473       ci.setProperty("SourceNamespace", CIMValue(sourceNamespace));
00474    }
00475    // create instance of filter
00476    reg.filter = hdl->createInstance(ns, ci);
00477    // get class of CIM_IndicationSubscription and new instance of it.
00478    // CIM_IndicationSubscription is an association class that connects
00479    // the IndicationFilter to the IndicationHandler.
00480    CIMClass cimClientFilterDelivery = hdl->getClass(ns,
00481          "CIM_IndicationSubscription", E_LOCAL_ONLY);
00482    ci = cimClientFilterDelivery.newInstance();
00483    // set the properties for the filter and the handler
00484    ci.setProperty("filter", CIMValue(reg.filter));
00485    ci.setProperty("handler", CIMValue(reg.handler));
00486    // creating the instance the CIM_IndicationSubscription creates
00487    // the event subscription
00488    reg.subscription = hdl->createInstance(ns, ci);
00489    //save info for deletion later and callback delivery
00490    reg.callback = cb;
00491    reg.ns = ns;
00492    reg.authCb = authCb;
00493    m_callbacks[httpPath] = reg;
00494    return httpPath;
00495 }
00496 */
00498 /*
00499 void
00500 CIMXMLListener::deregisterForIndication( const String& handle )
00501 {
00502    MutexLock lock(m_mutex);
00503    callbackMap_t::iterator i = m_callbacks.find(handle);
00504    if (i != m_callbacks.end())
00505    {
00506       registrationInfo reg = i->second;
00507       m_callbacks.erase(i);
00508       lock.release();
00509       m_pLAuthenticator->removeCredentials(reg.httpCredentials);
00510       deleteRegistrationObjects(reg);
00511    }
00512 }
00514 void
00515 CIMXMLListener::doIndicationOccurred( CIMInstance& ci,
00516       const String& listenerPath )
00517 {
00518    CIMListenerCallbackRef cb;
00519    { // scope for the MutexLock
00520       MutexLock lock(m_mutex);
00521       callbackMap_t::iterator i = m_callbacks.find(listenerPath);
00522       if (i == m_callbacks.end())
00523       {
00524          OW_THROWCIMMSG(CIMException::ACCESS_DENIED,
00525             Format("No listener for path: %1", listenerPath).c_str());
00526       }
00527       cb = i->second.callback;
00528    }
00529    cb->indicationOccurred( ci, listenerPath );
00530 }
00532 void
00533 CIMXMLListener::deleteRegistrationObjects( const registrationInfo& reg )
00534 {
00535    ClientCIMOMHandleRef hdl = ClientCIMOMHandle::createFromURL(reg.cimomUrl.toString(), reg.authCb);
00536    hdl->deleteInstance(reg.ns, reg.subscription);
00537    hdl->deleteInstance(reg.ns, reg.filter);
00538    hdl->deleteInstance(reg.ns, reg.handler);
00539 }
00540 */
00541 
00542 } // end namespace OW_NAMESPACE
00543 

Generated on Thu Feb 9 08:47:56 2006 for openwbem by  doxygen 1.4.6