LCOV - code coverage report
Current view: top level - framework/source/jobs - jobexecutor.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 89 138 64.5 %
Date: 2015-06-13 12:38:46 Functions: 12 17 70.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <jobs/job.hxx>
      21             : #include <jobs/joburl.hxx>
      22             : #include <jobs/configaccess.hxx>
      23             : #include <classes/converter.hxx>
      24             : #include <general.h>
      25             : #include <stdtypes.h>
      26             : 
      27             : #include "helper/mischelper.hxx"
      28             : 
      29             : #include <com/sun/star/beans/XPropertySet.hpp>
      30             : #include <com/sun/star/container/XNameAccess.hpp>
      31             : #include <com/sun/star/container/XContainer.hpp>
      32             : #include <com/sun/star/frame/ModuleManager.hpp>
      33             : #include <com/sun/star/task/XJobExecutor.hpp>
      34             : #include <com/sun/star/container/XContainerListener.hpp>
      35             : #include <com/sun/star/lang/XEventListener.hpp>
      36             : #include <com/sun/star/lang/XServiceInfo.hpp>
      37             : #include <com/sun/star/document/XEventListener.hpp>
      38             : #include <com/sun/star/frame/XModuleManager2.hpp>
      39             : 
      40             : #include <cppuhelper/basemutex.hxx>
      41             : #include <cppuhelper/compbase4.hxx>
      42             : #include <cppuhelper/supportsservice.hxx>
      43             : #include <unotools/configpaths.hxx>
      44             : #include <rtl/ref.hxx>
      45             : #include <rtl/ustrbuf.hxx>
      46             : #include <vcl/svapp.hxx>
      47             : 
      48             : using namespace framework;
      49             : 
      50             : namespace {
      51             : 
      52             : typedef cppu::WeakComponentImplHelper4<
      53             :           css::lang::XServiceInfo
      54             :         , css::task::XJobExecutor
      55             :         , css::container::XContainerListener // => lang.XEventListener
      56             :         , css::document::XEventListener >
      57             :     Base;
      58             : 
      59             : /**
      60             :     @short  implements a job executor, which can be triggered from any code
      61             :     @descr  It uses the given trigger event to locate any registered job service
      62             :             inside the configuration and execute it. Of course it controls the
      63             :             lifetime of such jobs too.
      64             :  */
      65             : class JobExecutor : private cppu::BaseMutex, public Base
      66             : {
      67             : private:
      68             : 
      69             :     /** reference to the uno service manager */
      70             :     css::uno::Reference< css::uno::XComponentContext > m_xContext;
      71             : 
      72             :     /** cached list of all registered event names of cfg for call optimization. */
      73             :     OUStringList m_lEvents;
      74             : 
      75             :     /** we listen at the configuration for changes at the event list. */
      76             :     ConfigAccess m_aConfig;
      77             : 
      78             :     /** helper to allow us listen to the configuration without a cyclic dependency */
      79             :     com::sun::star::uno::Reference<com::sun::star::container::XContainerListener> m_xConfigListener;
      80             : 
      81             :     virtual void SAL_CALL disposing() SAL_OVERRIDE;
      82             : 
      83             : public:
      84             : 
      85             :              JobExecutor( const css::uno::Reference< css::uno::XComponentContext >& xContext );
      86             :     virtual ~JobExecutor();
      87             : 
      88           1 :     virtual OUString SAL_CALL getImplementationName()
      89             :         throw (css::uno::RuntimeException, std::exception) SAL_OVERRIDE
      90             :     {
      91           1 :         return OUString("com.sun.star.comp.framework.JobExecutor");
      92             :     }
      93             : 
      94           0 :     virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName)
      95             :         throw (css::uno::RuntimeException, std::exception) SAL_OVERRIDE
      96             :     {
      97           0 :         return cppu::supportsService(this, ServiceName);
      98             :     }
      99             : 
     100           1 :     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames()
     101             :         throw (css::uno::RuntimeException, std::exception) SAL_OVERRIDE
     102             :     {
     103           1 :         css::uno::Sequence< OUString > aSeq(1);
     104           1 :         aSeq[0] = "com.sun.star.task.JobExecutor";
     105           1 :         return aSeq;
     106             :     }
     107             : 
     108             :     // task.XJobExecutor
     109             :     virtual void SAL_CALL trigger( const OUString& sEvent ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
     110             : 
     111             :     /// Initialization function after having acquire()'d.
     112             :     void initListeners();
     113             : 
     114             :     // document.XEventListener
     115             :     virtual void SAL_CALL notifyEvent( const css::document::EventObject& aEvent ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
     116             : 
     117             :     // container.XContainerListener
     118             :     virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
     119             :     virtual void SAL_CALL elementRemoved ( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
     120             :     virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
     121             : 
     122             :     // lang.XEventListener
     123             :     virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
     124             : };
     125             : 
     126             : /**
     127             :     @short      standard ctor
     128             :     @descr      It initialize this new instance.
     129             : 
     130             :     @param      xContext
     131             :                     reference to the uno service manager
     132             :  */
     133         196 : JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext )
     134             :     : Base                (m_aMutex)
     135             :     , m_xContext          (xContext                                                        )
     136         196 :     , m_aConfig           (xContext, "/org.openoffice.Office.Jobs/Events")
     137             : {
     138         196 : }
     139             : 
     140         196 : void JobExecutor::initListeners()
     141             : {
     142             :     // read the list of all currently registered events inside configuration.
     143             :     // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
     144             :     // We need it later to check if an incoming event request can be executed successfully
     145             :     // or must be rejected. It's an optimization! Of course we must implement updating of this
     146             :     // list too ... Be listener at the configuration.
     147             : 
     148         196 :     m_aConfig.open(ConfigAccess::E_READONLY);
     149         196 :     if (m_aConfig.getMode() == ConfigAccess::E_READONLY)
     150             :     {
     151             :         css::uno::Reference< css::container::XNameAccess > xRegistry(
     152         196 :                 m_aConfig.cfg(), css::uno::UNO_QUERY);
     153         196 :         if (xRegistry.is())
     154         392 :             m_lEvents = Converter::convert_seqOUString2OUStringList(
     155         392 :                     xRegistry->getElementNames());
     156             : 
     157             :         css::uno::Reference< css::container::XContainer > xNotifier(
     158         392 :                 m_aConfig.cfg(), css::uno::UNO_QUERY);
     159         196 :         if (xNotifier.is())
     160             :         {
     161         196 :             m_xConfigListener = new WeakContainerListener(this);
     162         196 :             xNotifier->addContainerListener(m_xConfigListener);
     163         196 :         }
     164             : 
     165             :         // don't close cfg here!
     166             :         // It will be done inside disposing ...
     167             :     }
     168         196 : }
     169             : 
     170         555 : JobExecutor::~JobExecutor()
     171             : {
     172         185 :     disposing();
     173         370 : }
     174             : 
     175         379 : void JobExecutor::disposing() {
     176         379 :     css::uno::Reference<css::container::XContainer> notifier;
     177         758 :     css::uno::Reference<css::container::XContainerListener> listener;
     178             :     {
     179         379 :         osl::MutexGuard g(rBHelper.rMutex);
     180         379 :         if (m_aConfig.getMode() != ConfigAccess::E_CLOSED) {
     181         194 :             notifier.set(m_aConfig.cfg(), css::uno::UNO_QUERY);
     182         194 :             listener = m_xConfigListener;
     183         194 :             m_aConfig.close();
     184             :         }
     185         379 :         m_xConfigListener.clear();
     186             :     }
     187         379 :     if (notifier.is()) {
     188         194 :         notifier->removeContainerListener(listener);
     189         379 :     }
     190         379 : }
     191             : 
     192             : /**
     193             :     @short  implementation of XJobExecutor interface
     194             :     @descr  We use the given event to locate any registered job inside our configuration
     195             :             and execute it. Further we control the lifetime of it and suppress
     196             :             shutdown of the office till all jobs was finished.
     197             : 
     198             :     @param  sEvent
     199             :                 is used to locate registered jobs
     200             :  */
     201         141 : void SAL_CALL JobExecutor::trigger( const OUString& sEvent ) throw(css::uno::RuntimeException, std::exception)
     202             : {
     203             :     SAL_INFO( "fwk", "JobExecutor::trigger()");
     204             : 
     205         141 :     css::uno::Sequence< OUString > lJobs;
     206             : 
     207             :     /* SAFE */ {
     208         141 :     osl::MutexGuard g(rBHelper.rMutex);
     209             : 
     210             :     // Optimization!
     211             :     // Check if the given event name exist inside configuration and reject wrong requests.
     212             :     // This optimization suppress using of the cfg api for getting event and job descriptions ...
     213         141 :     if (framework::find(m_lEvents, sEvent) == m_lEvents.end())
     214         282 :         return;
     215             : 
     216             :     // get list of all enabled jobs
     217             :     // The called static helper methods read it from the configuration and
     218             :     // filter disabled jobs using it's time stamp values.
     219           0 :     lJobs = JobData::getEnabledJobsForEvent(m_xContext, sEvent);
     220             :     } /* SAFE */
     221             : 
     222             :     // step over all enabled jobs and execute it
     223           0 :     sal_Int32 c = lJobs.getLength();
     224           0 :     for (sal_Int32 j=0; j<c; ++j)
     225             :     {
     226           0 :         rtl::Reference<Job> pJob;
     227             : 
     228             :         /* SAFE */ {
     229           0 :         SolarMutexGuard g2;
     230             : 
     231           0 :         JobData aCfg(m_xContext);
     232           0 :         aCfg.setEvent(sEvent, lJobs[j]);
     233           0 :         aCfg.setEnvironment(JobData::E_EXECUTION);
     234             : 
     235             :         /*Attention!
     236             :             Jobs implements interfaces and dies by ref count!
     237             :             And freeing of such uno object is done by uno itself.
     238             :             So we have to use dynamic memory everytimes.
     239             :          */
     240           0 :         pJob = new Job(m_xContext, css::uno::Reference< css::frame::XFrame >());
     241           0 :         pJob->setJobData(aCfg);
     242             :         } /* SAFE */
     243             : 
     244           0 :        pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
     245           0 :     }
     246             : }
     247             : 
     248       45665 : void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent ) throw(css::uno::RuntimeException, std::exception)
     249             : {
     250       45665 :     const char EVENT_ON_NEW[] = "OnNew";                            // Doc UI  event
     251       45665 :     const char EVENT_ON_LOAD[] = "OnLoad";                          // Doc UI  event
     252       45665 :     const char EVENT_ON_CREATE[] = "OnCreate";                      // Doc API event
     253       45665 :     const char EVENT_ON_LOAD_FINISHED[] = "OnLoadFinished";         // Doc API event
     254       45665 :     OUString EVENT_ON_DOCUMENT_OPENED("onDocumentOpened");   // Job UI  event : OnNew    or OnLoad
     255       91335 :     OUString EVENT_ON_DOCUMENT_ADDED("onDocumentAdded");     // Job API event : OnCreate or OnLoadFinished
     256             : 
     257       91335 :     OUString aModuleIdentifier;
     258       91334 :     ::std::vector< JobData::TJob2DocEventBinding > lJobs;
     259             : 
     260             :     /* SAFE */ {
     261       45668 :     osl::MutexGuard g(rBHelper.rMutex);
     262             : 
     263             :     // Optimization!
     264             :     // Check if the given event name exist inside configuration and reject wrong requests.
     265             :     // This optimization suppress using of the cfg api for getting event and job descriptions.
     266             :     // see using of m_lEvents.find() below ...
     267             : 
     268             :     // retrieve event context from event source
     269             :     try
     270             :     {
     271       46019 :         aModuleIdentifier = css::frame::ModuleManager::create( m_xContext )->identify( aEvent.Source );
     272             :     }
     273         356 :     catch( const css::uno::Exception& )
     274             :     {}
     275             : 
     276             :     // Special feature: If the events "OnNew" or "OnLoad" occurs - we generate our own event "onDocumentOpened".
     277       45668 :     if (
     278       90772 :         (aEvent.EventName == EVENT_ON_NEW) ||
     279       45104 :         (aEvent.EventName == EVENT_ON_LOAD)
     280             :        )
     281             :     {
     282         625 :         if (find(m_lEvents, EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end())
     283         625 :             JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_OPENED, lJobs);
     284             :     }
     285             : 
     286             :     // Special feature: If the events "OnCreate" or "OnLoadFinished" occurs - we generate our own event "onDocumentAdded".
     287       45668 :     if (
     288       90095 :         (aEvent.EventName == EVENT_ON_CREATE) ||
     289       44427 :         (aEvent.EventName == EVENT_ON_LOAD_FINISHED)
     290             :        )
     291             :     {
     292        4238 :         if (find(m_lEvents, EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end())
     293           0 :             JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_ADDED, lJobs);
     294             :     }
     295             : 
     296             :     // Add all jobs for "real" notified event too .-)
     297       45668 :     if (find(m_lEvents, aEvent.EventName) != m_lEvents.end())
     298           0 :         JobData::appendEnabledJobsForEvent(m_xContext, aEvent.EventName, lJobs);
     299             :     } /* SAFE */
     300             : 
     301             :     // step over all enabled jobs and execute it
     302       45668 :     ::std::vector< JobData::TJob2DocEventBinding >::const_iterator pIt;
     303      138876 :     for (  pIt  = lJobs.begin();
     304       92584 :            pIt != lJobs.end();
     305             :          ++pIt                 )
     306             :     {
     307         625 :         rtl::Reference<Job> pJob;
     308             : 
     309             :         /* SAFE */ {
     310         625 :         SolarMutexGuard g2;
     311             : 
     312         624 :         const JobData::TJob2DocEventBinding& rBinding = *pIt;
     313             : 
     314         637 :         JobData aCfg(m_xContext);
     315         624 :         aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName);
     316         624 :         aCfg.setEnvironment(JobData::E_DOCUMENTEVENT);
     317             : 
     318         624 :         if (!aCfg.hasCorrectContext(aModuleIdentifier))
     319         611 :             continue;
     320             : 
     321             :         /*Attention!
     322             :             Jobs implements interfaces and dies by ref count!
     323             :             And freeing of such uno object is done by uno itself.
     324             :             So we have to use dynamic memory everytimes.
     325             :          */
     326          26 :         css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY);
     327          13 :         pJob = new Job(m_xContext, xModel);
     328          26 :         pJob->setJobData(aCfg);
     329             :         } /* SAFE */
     330             : 
     331          13 :         pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
     332       45680 :     }
     333       45667 : }
     334             : 
     335           0 : void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException, std::exception)
     336             : {
     337           0 :     OUString sValue;
     338           0 :     if (aEvent.Accessor >>= sValue)
     339             :     {
     340           0 :         OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
     341           0 :         if (!sEvent.isEmpty())
     342             :         {
     343           0 :             OUStringList::iterator pEvent = find(m_lEvents, sEvent);
     344           0 :             if (pEvent == m_lEvents.end())
     345           0 :                 m_lEvents.push_back(sEvent);
     346           0 :         }
     347           0 :     }
     348           0 : }
     349             : 
     350           0 : void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException, std::exception)
     351             : {
     352           0 :     OUString sValue;
     353           0 :     if (aEvent.Accessor >>= sValue)
     354             :     {
     355           0 :         OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
     356           0 :         if (!sEvent.isEmpty())
     357             :         {
     358           0 :             OUStringList::iterator pEvent = find(m_lEvents, sEvent);
     359           0 :             if (pEvent != m_lEvents.end())
     360           0 :                 m_lEvents.erase(pEvent);
     361           0 :         }
     362           0 :     }
     363           0 : }
     364             : 
     365           0 : void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& ) throw(css::uno::RuntimeException, std::exception)
     366             : {
     367             :     // I'm not interested on changed items :-)
     368           0 : }
     369             : 
     370             : /** @short  the used cfg changes notifier wish to be released in its reference.
     371             : 
     372             :     @descr  We close our internal used configuration instance to
     373             :             free this reference.
     374             : 
     375             :     @attention  For the special feature "bind global document event broadcaster to job execution"
     376             :                 this job executor instance was registered from outside code as
     377             :                 css.document.XEventListener. So it can be, that this disposing call comes from
     378             :                 the global event broadcaster service. But we don't hold any reference to this service
     379             :                 which can or must be released. Because this broadcaster itself is an one instance service
     380             :                 too, we can ignore this request. On the other side we must relase our internal CFG
     381             :                 reference ... SOLUTION => check the given event source and react only, if it's our internal
     382             :                 hold configuration object!
     383             :  */
     384           0 : void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException, std::exception)
     385             : {
     386             :     /* SAFE { */
     387           0 :     osl::MutexGuard g(rBHelper.rMutex);
     388           0 :     css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY);
     389           0 :     if (
     390           0 :         (xCFG                == aEvent.Source        ) &&
     391           0 :         (m_aConfig.getMode() != ConfigAccess::E_CLOSED)
     392             :        )
     393             :     {
     394           0 :         m_aConfig.close();
     395           0 :     }
     396             :     /* } SAFE */
     397           0 : }
     398             : 
     399         196 : struct Instance {
     400         196 :     explicit Instance(
     401             :         css::uno::Reference<css::uno::XComponentContext> const & context):
     402             :         instance(
     403         196 :             static_cast<cppu::OWeakObject *>(new JobExecutor(context)))
     404             :     {
     405             :         // 2nd phase initialization needed
     406             :         static_cast<JobExecutor *>(static_cast<cppu::OWeakObject *>
     407         196 :                 (instance.get()))->initListeners();
     408         196 :     }
     409             : 
     410             :     rtl::Reference<css::uno::XInterface> instance;
     411             : };
     412             : 
     413             : struct Singleton:
     414             :     public rtl::StaticWithArg<
     415             :         Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton>
     416             : {};
     417             : 
     418             : }
     419             : 
     420             : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * SAL_CALL
     421         197 : com_sun_star_comp_framework_JobExecutor_get_implementation(
     422             :     css::uno::XComponentContext *context,
     423             :     css::uno::Sequence<css::uno::Any> const &)
     424             : {
     425             :     return cppu::acquire(static_cast<cppu::OWeakObject *>(
     426         197 :                 Singleton::get(context).instance.get()));
     427             : }
     428             : 
     429             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11