diff options
Diffstat (limited to 'framework/source/jobs')
-rw-r--r-- | framework/source/jobs/configaccess.cxx | 242 | ||||
-rw-r--r-- | framework/source/jobs/helponstartup.cxx | 423 | ||||
-rw-r--r-- | framework/source/jobs/job.cxx | 944 | ||||
-rw-r--r-- | framework/source/jobs/jobconst.cxx | 74 | ||||
-rw-r--r-- | framework/source/jobs/jobdata.cxx | 714 | ||||
-rw-r--r-- | framework/source/jobs/jobdispatch.cxx | 475 | ||||
-rw-r--r-- | framework/source/jobs/jobexecutor.cxx | 364 | ||||
-rw-r--r-- | framework/source/jobs/jobresult.cxx | 261 | ||||
-rw-r--r-- | framework/source/jobs/joburl.cxx | 657 | ||||
-rw-r--r-- | framework/source/jobs/makefile.mk | 53 | ||||
-rw-r--r-- | framework/source/jobs/shelljob.cxx | 214 |
11 files changed, 4421 insertions, 0 deletions
diff --git a/framework/source/jobs/configaccess.cxx b/framework/source/jobs/configaccess.cxx new file mode 100644 index 000000000000..95226268ce2b --- /dev/null +++ b/framework/source/jobs/configaccess.cxx @@ -0,0 +1,242 @@ + /************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes +#include <jobs/configaccess.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <threadhelp/resetableguard.hxx> +#include <general.h> +#include <services.h> + +//________________________________ +// interface includes +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> + +//________________________________ +// includes of other projects +#include <unotools/configpathes.hxx> +#include <rtl/ustrbuf.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +//________________________________ +/** + @short open the configuration of this job + @descr We open the configuration of this job only. Not the whole package or the whole + job set. We are interested on our own properties only. + We set the opened configuration access as our member. So any following method, + which needs cfg access, can use it. That prevent us against multiple open/close requests. + But you can use this method to upgrade an already opened configuration too. + + @param eMode + force opening of the configuration access in readonly or in read/write mode + */ +ConfigAccess::ConfigAccess( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR , + /*IN*/ const ::rtl::OUString& sRoot ) + : ThreadHelpBase( ) + , m_xSMGR ( xSMGR ) + , m_sRoot ( sRoot ) + , m_eMode ( E_CLOSED ) +{ +} + +//________________________________ +/** + @short last chance to close an open configuration access point + @descr In case our user forgot to close this configuration point + in the right way, normaly he will run into some trouble - + e.g. losing data. + */ +ConfigAccess::~ConfigAccess() +{ + close(); +} + +//________________________________ +/** + @short return the internal mode of this instance + @descr May be the outside user need any information about successfully opened + or closed config access point objects. He can control the internal mode to do so. + + @return The internal open state of this object. + */ +ConfigAccess::EOpenMode ConfigAccess::getMode() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_eMode; + /* } SAFE */ +} + +//________________________________ +/** + @short open the configuration access in the specified mode + @descr We set the opened configuration access as our member. So any following method, + which needs cfg access, can use it. That prevent us against multiple open/close requests. + But you can use this method to upgrade an already opened configuration too. + It's possible to open a config access in READONLY mode first and "open" it at a second + time within the mode READWRITE. Then we will upgrade it. Dowgrade will be possible too. + + But note: closing will be done explicitly by calling method close() ... not by + downgrading with mode CLOSED! + + @param eMode + force (re)opening of the configuration access in readonly or in read/write mode + */ +void ConfigAccess::open( /*IN*/ EOpenMode eMode ) +{ + /* SAFE { */ + // We must lock the whole method to be shure, that nobody + // outside uses our internal member m_xAccess! + WriteGuard aWriteLock(m_aLock); + + // check if configuration is already open in the right mode. + // By the way: Don't allow closing by using this method! + if ( + (eMode !=E_CLOSED) && + (m_eMode!=eMode ) + ) + { + // We have to close the old access point without any question here. + // It will be open again using the new mode. + // can be called without checks! It does the checks by itself ... + // e.g. for already closed or not opened configuration. + // Flushing of all made changes will be done here too. + close(); + + // create the configuration provider, which provides sub access points + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider(m_xSMGR->createInstance(SERVICENAME_CFGPROVIDER), css::uno::UNO_QUERY); + if (xConfigProvider.is()) + { + css::beans::PropertyValue aParam; + aParam.Name = DECLARE_ASCII("nodepath"); + aParam.Value <<= m_sRoot; + + css::uno::Sequence< css::uno::Any > lParams(1); + lParams[0] <<= aParam; + + // open it + try + { + if (eMode==E_READONLY) + m_xConfig = xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGREADACCESS , lParams); + else + if (eMode==E_READWRITE) + m_xConfig = xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGUPDATEACCESS, lParams); + } + catch(css::uno::Exception& ex) + { + (void) ex; // avoid warning + LOG_WARNING("open config ...", U2B(ex.Message)) + } + + m_eMode = E_CLOSED; + if (m_xConfig.is()) + m_eMode = eMode; + } + } + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short close the internal opened configuration access and flush all changes + @descr It checks, if the given access is valid and react in the right way. + It flushes all changes ... so nobody else must know this state. + */ +void ConfigAccess::close() +{ + /* SAFE { */ + // Lock the whole method, to be shure that nobody else uses our internal members + // during this time. + WriteGuard aWriteLock(m_aLock); + + // check already closed configuration + if (m_xConfig.is()) + { + css::uno::Reference< css::util::XChangesBatch > xFlush(m_xConfig, css::uno::UNO_QUERY); + if (xFlush.is()) + xFlush->commitChanges(); + m_xConfig = css::uno::Reference< css::uno::XInterface >(); + m_eMode = E_CLOSED; + } + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short provides an access to the internal wrapped configuration access + @descr It's not allowed to safe this c++ (!) reference outside. You have + to use it directly. Further you must use our public lock member m_aLock + to synchronize your code with our internal structures and our interface + methods. Acquire it before you call cfg() and release it afterwards immediatly. + + E.g.: ConfigAccess aAccess(...); + ReadGuard aReadLock(aAccess.m_aLock); + Reference< XPropertySet > xSet(aAccess.cfg(), UNO_QUERY); + Any aProp = xSet->getPropertyValue("..."); + aReadLock.unlock(); + + @attention During this time it's not allowed to call the methods open() or close()! + Otherwhise you will change your own referenced config access. Anything will + be possible then. + + @return A c++(!) reference to the uno instance of the configuration access point. + */ +const css::uno::Reference< css::uno::XInterface >& ConfigAccess::cfg() +{ + // must be synchronized from outside! + // => no lock here ... + return m_xConfig; +} + +} // namespace framework diff --git a/framework/source/jobs/helponstartup.cxx b/framework/source/jobs/helponstartup.cxx new file mode 100644 index 000000000000..93ea64e94cb4 --- /dev/null +++ b/framework/source/jobs/helponstartup.cxx @@ -0,0 +1,423 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_______________________________________________ +// include own header +#include <jobs/helponstartup.hxx> +#include <threadhelp/resetableguard.hxx> +#include <loadenv/targethelper.hxx> +#include <services.h> + +//_______________________________________________ +// include others +#include <comphelper/configurationhelper.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <rtl/ustrbuf.hxx> + +//_______________________________________________ +// include interfaces +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/frame/XDesktop.hpp> + +//_______________________________________________ +// namespace + +namespace framework{ + +//_______________________________________________ +// definitions + +// path to module config +static ::rtl::OUString CFG_PACKAGE_MODULES = ::rtl::OUString::createFromAscii("/org.openoffice.Setup/Office/Factories"); +static ::rtl::OUString CFG_PACKAGE_SETUP = ::rtl::OUString::createFromAscii("/org.openoffice.Setup" ); +static ::rtl::OUString CFG_PACKAGE_COMMON = ::rtl::OUString::createFromAscii("/org.openoffice.Office.Common" ); +static ::rtl::OUString CFG_PATH_L10N = ::rtl::OUString::createFromAscii("L10N" ); +static ::rtl::OUString CFG_PATH_HELP = ::rtl::OUString::createFromAscii("Help" ); +static ::rtl::OUString CFG_KEY_LOCALE = ::rtl::OUString::createFromAscii("ooLocale" ); +static ::rtl::OUString CFG_KEY_HELPSYSTEM = ::rtl::OUString::createFromAscii("System" ); + +// props of job environment +static ::rtl::OUString PROP_ENVIRONMENT = ::rtl::OUString::createFromAscii("Environment" ); +static ::rtl::OUString PROP_JOBCONFIG = ::rtl::OUString::createFromAscii("JobConfig" ); +static ::rtl::OUString PROP_ENVTYPE = ::rtl::OUString::createFromAscii("EnvType" ); +static ::rtl::OUString PROP_MODEL = ::rtl::OUString::createFromAscii("Model" ); + +// props of module config +static ::rtl::OUString PROP_HELP_BASEURL = ::rtl::OUString::createFromAscii("ooSetupFactoryHelpBaseURL" ); +static ::rtl::OUString PROP_AUTOMATIC_HELP = ::rtl::OUString::createFromAscii("ooSetupFactoryHelpOnOpen" ); + +// special value of job environment +static ::rtl::OUString ENVTYPE_DOCUMENTEVENT = ::rtl::OUString::createFromAscii("DOCUMENTEVENT" ); + +//----------------------------------------------- + +DEFINE_XSERVICEINFO_MULTISERVICE(HelpOnStartup , + ::cppu::OWeakObject , + SERVICENAME_JOB , + IMPLEMENTATIONNAME_HELPONSTARTUP) + +DEFINE_INIT_SERVICE(HelpOnStartup, + { + /* Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + // create some needed uno services and cache it + m_xModuleManager = css::uno::Reference< css::frame::XModuleManager >( + m_xSMGR->createInstance(SERVICENAME_MODULEMANAGER), + css::uno::UNO_QUERY_THROW); + + m_xDesktop = css::uno::Reference< css::frame::XFrame >( + m_xSMGR->createInstance(SERVICENAME_DESKTOP), + css::uno::UNO_QUERY_THROW); + + m_xConfig = css::uno::Reference< css::container::XNameAccess >( + ::comphelper::ConfigurationHelper::openConfig( + m_xSMGR, + CFG_PACKAGE_MODULES, + ::comphelper::ConfigurationHelper::E_READONLY), + css::uno::UNO_QUERY_THROW); + + // ask for office locale + ::comphelper::ConfigurationHelper::readDirectKey( + m_xSMGR, + CFG_PACKAGE_SETUP, + CFG_PATH_L10N, + CFG_KEY_LOCALE, + ::comphelper::ConfigurationHelper::E_READONLY) >>= m_sLocale; + + // detect system + ::comphelper::ConfigurationHelper::readDirectKey( + m_xSMGR, + CFG_PACKAGE_COMMON, + CFG_PATH_HELP, + CFG_KEY_HELPSYSTEM, + ::comphelper::ConfigurationHelper::E_READONLY) >>= m_sSystem; + + // Start listening for disposing events of these services, + // so we can react e.g. for an office shutdown + css::uno::Reference< css::lang::XComponent > xComponent; + xComponent = css::uno::Reference< css::lang::XComponent >(m_xModuleManager, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this)); + xComponent = css::uno::Reference< css::lang::XComponent >(m_xDesktop, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this)); + xComponent = css::uno::Reference< css::lang::XComponent >(m_xConfig, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this)); + } + ) + +//----------------------------------------------- +HelpOnStartup::HelpOnStartup(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) + : ThreadHelpBase( ) + , m_xSMGR (xSMGR) +{ +} + +//----------------------------------------------- +HelpOnStartup::~HelpOnStartup() +{ +} + +//----------------------------------------------- +// css.task.XJob +css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments) + throw(css::lang::IllegalArgumentException, + css::uno::Exception , + css::uno::RuntimeException ) +{ + // Analyze the given arguments; try to locate a model there and + // classify it's used application module. + ::rtl::OUString sModule = its_getModuleIdFromEnv(lArguments); + + // Attention: We are bound to events for openeing any document inside the office. + // That includes e.g. the help module itself. But we have to do nothing then! + if (!sModule.getLength()) + return css::uno::Any(); + + // check current state of the help module + // a) help isnt open => show default page for the detected module + // b) help shows any other default page(!) => show default page for the detected module + // c) help shows any other content => do nothing (user travelled to any other content and leaved the set of default pages) + ::rtl::OUString sCurrentHelpURL = its_getCurrentHelpURL(); + sal_Bool bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL); + sal_Bool bShowIt = sal_False; + + // a) + if (!sCurrentHelpURL.getLength()) + bShowIt = sal_True; + else + // b) + if (bCurrentHelpURLIsAnyDefaultURL) + bShowIt = sal_True; + + if (bShowIt) + { + // retrieve the help URL for the detected application module + ::rtl::OUString sModuleDependendHelpURL = its_checkIfHelpEnabledAndGetURL(sModule); + if (sModuleDependendHelpURL.getLength()) + { + // Show this help page. + // Note: The help window brings itself to front ... + Help* pHelp = Application::GetHelp(); + if (pHelp) + pHelp->Start(sModuleDependendHelpURL, 0); + } + } + + return css::uno::Any(); +} + +//----------------------------------------------- +void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent) + throw(css::uno::RuntimeException) +{ + // SAFE -> + ResetableGuard aLock(m_aLock); + + if (aEvent.Source == m_xModuleManager) + m_xModuleManager.clear(); + else + if (aEvent.Source == m_xDesktop) + m_xDesktop.clear(); + else + if (aEvent.Source == m_xConfig) + m_xConfig.clear(); + + aLock.unlock(); + // <- SAFE +} + +//----------------------------------------------- +::rtl::OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments) +{ + ::comphelper::SequenceAsHashMap lArgs (lArguments); + ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault(PROP_ENVIRONMENT, css::uno::Sequence< css::beans::NamedValue >()); + ::comphelper::SequenceAsHashMap lJobConfig = lArgs.getUnpackedValueOrDefault(PROP_JOBCONFIG , css::uno::Sequence< css::beans::NamedValue >()); + + // check for right environment. + // If its not a DocumentEvent, which triggered this job, + // we cant work correctly! => return immediatly and do nothing + ::rtl::OUString sEnvType = lEnvironment.getUnpackedValueOrDefault(PROP_ENVTYPE, ::rtl::OUString()); + if (!sEnvType.equals(ENVTYPE_DOCUMENTEVENT)) + return ::rtl::OUString(); + + css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault(PROP_MODEL, css::uno::Reference< css::frame::XModel >()); + if (!xDoc.is()) + return ::rtl::OUString(); + + // be sure that we work on top level documents only, which are registered + // on the desktop instance. Ignore e.g. life previews, which are top frames too ... + // but not registered at this global desktop instance. + css::uno::Reference< css::frame::XDesktop > xDesktopCheck; + css::uno::Reference< css::frame::XFrame > xFrame ; + css::uno::Reference< css::frame::XController > xController = xDoc->getCurrentController(); + if (xController.is()) + xFrame = xController->getFrame(); + if (xFrame.is() && xFrame->isTop()) + xDesktopCheck = css::uno::Reference< css::frame::XDesktop >(xFrame->getCreator(), css::uno::UNO_QUERY); + if (!xDesktopCheck.is()) + return ::rtl::OUString(); + + // OK - now we are sure this document is a top level document. + // Classify it. + // SAFE -> + ResetableGuard aLock(m_aLock); + css::uno::Reference< css::frame::XModuleManager > xModuleManager = m_xModuleManager; + aLock.unlock(); + // <- SAFE + + if (!xModuleManager.is()) + return ::rtl::OUString(); + + ::rtl::OUString sModuleId; + try + { + sModuleId = xModuleManager->identify(xDoc); + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + { sModuleId = ::rtl::OUString(); } + + return sModuleId; +} + +//----------------------------------------------- +::rtl::OUString HelpOnStartup::its_getCurrentHelpURL() +{ + // SAFE -> + ResetableGuard aLock(m_aLock); + css::uno::Reference< css::frame::XFrame > xDesktop = m_xDesktop; + aLock.unlock(); + // <- SAFE + + if (!xDesktop.is()) + return ::rtl::OUString(); + + css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN); + if (!xHelp.is()) + return ::rtl::OUString(); + + ::rtl::OUString sCurrentHelpURL; + try + { + css::uno::Reference< css::frame::XFramesSupplier > xHelpRoot (xHelp , css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XIndexAccess > xHelpChilds(xHelpRoot->getFrames(), css::uno::UNO_QUERY_THROW); + + css::uno::Reference< css::frame::XFrame > xHelpChild ; + css::uno::Reference< css::frame::XController > xHelpView ; + css::uno::Reference< css::frame::XModel > xHelpContent; + + xHelpChilds->getByIndex(0) >>= xHelpChild; + if (xHelpChild.is()) + xHelpView = xHelpChild->getController(); + if (xHelpView.is()) + xHelpContent = xHelpView->getModel(); + if (xHelpContent.is()) + sCurrentHelpURL = xHelpContent->getURL(); + } + catch(css::uno::RuntimeException& exRun) + { throw exRun; } + catch(css::uno::Exception&) + { sCurrentHelpURL = ::rtl::OUString(); } + + return sCurrentHelpURL; +} + +//----------------------------------------------- +::sal_Bool HelpOnStartup::its_isHelpUrlADefaultOne(const ::rtl::OUString& sHelpURL) +{ + if (!sHelpURL.getLength()) + return sal_False; + + // SAFE -> + ResetableGuard aLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR (m_xSMGR, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig; + ::rtl::OUString sLocale = m_sLocale; + ::rtl::OUString sSystem = m_sSystem; + aLock.unlock(); + // <- SAFE + + if (!xConfig.is()) + return sal_False; + + // check given help url against all default ones + const css::uno::Sequence< ::rtl::OUString > lModules = xConfig->getElementNames(); + const ::rtl::OUString* pModules = lModules.getConstArray(); + ::sal_Int32 c = lModules.getLength(); + ::sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + try + { + css::uno::Reference< css::container::XNameAccess > xModuleConfig; + xConfig->getByName(pModules[i]) >>= xModuleConfig; + if (!xModuleConfig.is()) + continue; + + ::rtl::OUString sHelpBaseURL; + xModuleConfig->getByName(PROP_HELP_BASEURL) >>= sHelpBaseURL; + ::rtl::OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem); + if (sHelpURL.equals(sHelpURLForModule)) + return sal_True; + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + {} + } + + return sal_False; +} + +//----------------------------------------------- +::rtl::OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const ::rtl::OUString& sModule) +{ + // SAFE -> + ResetableGuard aLock(m_aLock); + css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig; + ::rtl::OUString sLocale = m_sLocale; + ::rtl::OUString sSystem = m_sSystem; + aLock.unlock(); + // <- SAFE + + ::rtl::OUString sHelpURL; + + try + { + css::uno::Reference< css::container::XNameAccess > xModuleConfig; + if (xConfig.is()) + xConfig->getByName(sModule) >>= xModuleConfig; + + sal_Bool bHelpEnabled = sal_False; + if (xModuleConfig.is()) + xModuleConfig->getByName(PROP_AUTOMATIC_HELP) >>= bHelpEnabled; + + if (bHelpEnabled) + { + ::rtl::OUString sHelpBaseURL; + xModuleConfig->getByName(PROP_HELP_BASEURL) >>= sHelpBaseURL; + sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem); + } + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + { sHelpURL = ::rtl::OUString(); } + + return sHelpURL; +} + +//----------------------------------------------- +::rtl::OUString HelpOnStartup::ist_createHelpURL(const ::rtl::OUString& sBaseURL, + const ::rtl::OUString& sLocale , + const ::rtl::OUString& sSystem ) +{ + ::rtl::OUStringBuffer sHelpURL(256); + sHelpURL.append (sBaseURL ); + sHelpURL.appendAscii("?Language="); + sHelpURL.append (sLocale ); + sHelpURL.appendAscii("&System=" ); + sHelpURL.append (sSystem ); + + return sHelpURL.makeStringAndClear(); +} + +} // namespace framework diff --git a/framework/source/jobs/job.cxx b/framework/source/jobs/job.cxx new file mode 100644 index 000000000000..7ae2452874be --- /dev/null +++ b/framework/source/jobs/job.cxx @@ -0,0 +1,944 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes +#include <jobs/job.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <general.h> +#include <services.h> + +//________________________________ +// interface includes +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/task/XAsyncJob.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +//________________________________ +// includes of other projects +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +DEFINE_XINTERFACE_4( Job , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::task::XJobListener ), + DIRECT_INTERFACE(css::frame::XTerminateListener), + DIRECT_INTERFACE(css::util::XCloseListener ) + ) + +DEFINE_XTYPEPROVIDER_4( Job , + css::lang::XTypeProvider , + css::task::XJobListener , + css::frame::XTerminateListener, + css::util::XCloseListener + ) + +//________________________________ +/** + @short standard ctor + @descr It initialize this new instance. But it set some generic parameters here only. + Specialized informations (e.g. the alias or service name ofthis job) will be set + later using the method setJobData(). + + @param xSMGR + reference to the uno service manager + + @param xFrame + reference to the frame, in which environment we run + (May be null!) +*/ +Job::Job( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR , + /*IN*/ const css::uno::Reference< css::frame::XFrame >& xFrame ) + : ThreadHelpBase (&Application::GetSolarMutex()) + , ::cppu::OWeakObject ( ) + , m_aJobCfg (xSMGR ) + , m_xSMGR (xSMGR ) + , m_xFrame (xFrame ) + , m_bListenOnDesktop (sal_False ) + , m_bListenOnFrame (sal_False ) + , m_bListenOnModel (sal_False ) + , m_bPendingCloseFrame (sal_False ) + , m_bPendingCloseModel (sal_False ) + , m_eRunState (E_NEW ) +{ +} + +//________________________________ +/** + @short standard ctor + @descr It initialize this new instance. But it set some generic parameters here only. + Specialized informations (e.g. the alias or service name ofthis job) will be set + later using the method setJobData(). + + @param xSMGR + reference to the uno service manager + + @param xModel + reference to the model, in which environment we run + (May be null!) +*/ +Job::Job( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR , + /*IN*/ const css::uno::Reference< css::frame::XModel >& xModel ) + : ThreadHelpBase (&Application::GetSolarMutex()) + , ::cppu::OWeakObject ( ) + , m_aJobCfg (xSMGR ) + , m_xSMGR (xSMGR ) + , m_xModel (xModel ) + , m_bListenOnDesktop (sal_False ) + , m_bListenOnFrame (sal_False ) + , m_bListenOnModel (sal_False ) + , m_bPendingCloseFrame (sal_False ) + , m_bPendingCloseModel (sal_False ) + , m_eRunState (E_NEW ) +{ +} + +//________________________________ +/** + @short superflous! + @descr Releasing of memory and reference must be done inside die() call. + Otherwhise it's a bug. +*/ +Job::~Job() +{ +} + +//________________________________ +/** + @short set (or delete) a listener for sending dispatch result events + @descr Because this object is used in a wrapped mode ... the original listener + for such events can't be registered here directly. Because the + listener expect to get the original object given as source of the event. + That's why we get this source here too, to fake(!) it at sending time! + + @param xListener + the original listener for dispatch result events + + @param xSourceFake + our user, which got the registration request for this listener +*/ +void Job::setDispatchResultFake( /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener , + /*IN*/ const css::uno::Reference< css::uno::XInterface >& xSourceFake ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // reject dangerous calls + if (m_eRunState != E_NEW) + { + LOG_WARNING("Job::setJobData()", "job may still running or already finished") + return; + } + + m_xResultListener = xListener ; + m_xResultSourceFake = xSourceFake; + aWriteLock.unlock(); + /* } SAFE */ +} + +void Job::setJobData( const JobData& aData ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // reject dangerous calls + if (m_eRunState != E_NEW) + { + LOG_WARNING("Job::setJobData()", "job may still running or already finished") + return; + } + + m_aJobCfg = aData; + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short runs the job + @descr It doesn't matter, if the job is an asynchronous or + synchronous one. This method returns only if it was finished + or cancelled. + + @param lDynamicArgs + optional arguments for job execution + In case the represented job is a configured one (which uses static + arguments too) all informations will be merged! +*/ +void Job::execute( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // reject dangerous calls + if (m_eRunState != E_NEW) + { + LOG_WARNING("Job::execute()", "job may still running or already finished") + return; + } + + // create the environment and mark this job as running ... + m_eRunState = E_RUNNING; + impl_startListening(); + + css::uno::Reference< css::task::XAsyncJob > xAJob; + css::uno::Reference< css::task::XJob > xSJob; + css::uno::Sequence< css::beans::NamedValue > lJobArgs = impl_generateJobArgs(lDynamicArgs); + + // It's neccessary to hold us self alive! + // Otherwhise we might die by ref count ... + css::uno::Reference< css::task::XJobListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + + try + { + // create the job + // We must check for the supported interface on demand! + // But we preferr the synchronous one ... + m_xJob = m_xSMGR->createInstance(m_aJobCfg.getService()); + xSJob = css::uno::Reference< css::task::XJob >(m_xJob, css::uno::UNO_QUERY); + if (!xSJob.is()) + xAJob = css::uno::Reference< css::task::XAsyncJob >(m_xJob, css::uno::UNO_QUERY); + + // execute it asynchron + if (xAJob.is()) + { + m_aAsyncWait.reset(); + aWriteLock.unlock(); + /* } SAFE */ + xAJob->executeAsync(lJobArgs, xThis); + // wait for finishing this job - so this method + // does the same for synchronous and asynchronous jobs! + m_aAsyncWait.wait(); + aWriteLock.lock(); + /* SAFE { */ + // Note: Result handling was already done inside the callback! + } + // execute it synchron + else if (xSJob.is()) + { + aWriteLock.unlock(); + /* } SAFE */ + css::uno::Any aResult = xSJob->execute(lJobArgs); + aWriteLock.lock(); + /* SAFE { */ + impl_reactForJobResult(aResult); + } + } + #if OSL_DEBUG_LEVEL > 0 + catch(const css::uno::Exception& ex) + { + ::rtl::OUStringBuffer sMsg(256); + sMsg.appendAscii("Got exception during job execution. Original Message was:\n\""); + sMsg.append (ex.Message); + sMsg.appendAscii("\""); + LOG_WARNING("Job::execute()", U2B(sMsg.makeStringAndClear()).getStr()) + } + #else + catch(const css::uno::Exception&) + {} + #endif + + // deinitialize the environment and mark this job as finished ... + // but don't overwrite any informations about STOPPED or might DISPOSED jobs! + impl_stopListening(); + if (m_eRunState == E_RUNNING) + m_eRunState = E_STOPPED_OR_FINISHED; + + // If we got a close request from our frame or model ... + // but we disagreed wit that by throwing a veto exception... + // and got the ownership ... + // we have to close the resource frame or model now - + // and to disable ourself! + if (m_bPendingCloseFrame) + { + m_bPendingCloseFrame = sal_False; + css::uno::Reference< css::util::XCloseable > xClose(m_xFrame, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(sal_True); + } + catch(const css::util::CloseVetoException&) {} + } + } + + if (m_bPendingCloseModel) + { + m_bPendingCloseModel = sal_False; + css::uno::Reference< css::util::XCloseable > xClose(m_xModel, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(sal_True); + } + catch(const css::util::CloseVetoException&) {} + } + } + + aWriteLock.unlock(); + /* SAFE { */ + + // release this instance ... + die(); +} + +//________________________________ +/** + @short kill this job + @descr It doesn't matter if this request is called from inside or + from outside. We release our internal structures and stop + avary activity. After doing so - this instance will not be + useable any longer! Of course we try to handle further requests + carefully. May somehwere else hold a reference to us ... +*/ +void Job::die() +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + impl_stopListening(); + + if (m_eRunState != E_DISPOSED) + { + try + { + css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY); + if (xDispose.is()) + { + xDispose->dispose(); + m_eRunState = E_DISPOSED; + } + } + catch(const css::lang::DisposedException&) + { + m_eRunState = E_DISPOSED; + } + } + + m_xJob = css::uno::Reference< css::uno::XInterface >(); + m_xFrame = css::uno::Reference< css::frame::XFrame >(); + m_xModel = css::uno::Reference< css::frame::XModel >(); + m_xDesktop = css::uno::Reference< css::frame::XDesktop >(); + m_xResultListener = css::uno::Reference< css::frame::XDispatchResultListener >(); + m_xResultSourceFake = css::uno::Reference< css::uno::XInterface >(); + m_bPendingCloseFrame = sal_False; + m_bPendingCloseModel = sal_False; + + aWriteLock.unlock(); + /* SAFE { */ +} + +//________________________________ +/** + @short generates list of arguments for job execute + @descr There exist a set of informations, which can be needed by a job. + a) it's static configuration data (Equals for all jobs. ) + b) it's specific configuration data (Different for every job.) + c) some environment values (e.g. the frame, for which this job was started) + d) any other dynamic data (e.g. parameters of a dispatch() request) + We collect all these informations and generate one list which include all others. + + @param lDynamicArgs + list of dynamic arguments (given by a corresponding dispatch() call) + Can be empty too. + + @return A list which includes all mentioned sub lists. +*/ +css::uno::Sequence< css::beans::NamedValue > Job::impl_generateJobArgs( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs ) +{ + css::uno::Sequence< css::beans::NamedValue > lAllArgs; + + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + // the real structure of the returned list depends from the environment of this job! + JobData::EMode eMode = m_aJobCfg.getMode(); + + // Create list of environment variables. This list must be part of the + // returned structure everytimes ... but some of its members are opetional! + css::uno::Sequence< css::beans::NamedValue > lEnvArgs(1); + lEnvArgs[0].Name = ::rtl::OUString::createFromAscii(JobData::PROP_ENVTYPE); + lEnvArgs[0].Value <<= m_aJobCfg.getEnvironmentDescriptor(); + + if (m_xFrame.is()) + { + sal_Int32 c = lEnvArgs.getLength(); + lEnvArgs.realloc(c+1); + lEnvArgs[c].Name = ::rtl::OUString::createFromAscii(JobData::PROP_FRAME); + lEnvArgs[c].Value <<= m_xFrame; + } + if (m_xModel.is()) + { + sal_Int32 c = lEnvArgs.getLength(); + lEnvArgs.realloc(c+1); + lEnvArgs[c].Name = ::rtl::OUString::createFromAscii(JobData::PROP_MODEL); + lEnvArgs[c].Value <<= m_xModel; + } + if (eMode==JobData::E_EVENT) + { + sal_Int32 c = lEnvArgs.getLength(); + lEnvArgs.realloc(c+1); + lEnvArgs[c].Name = ::rtl::OUString::createFromAscii(JobData::PROP_EVENTNAME); + lEnvArgs[c].Value <<= m_aJobCfg.getEvent(); + } + + // get the configuration data from the job data container ... if possible + // Means: if this job has any configuration data. Note: only realy + // filled lists will be set to the return structure at the end of this method. + css::uno::Sequence< css::beans::NamedValue > lConfigArgs ; + css::uno::Sequence< css::beans::NamedValue > lJobConfigArgs; + if (eMode==JobData::E_ALIAS || eMode==JobData::E_EVENT) + { + lConfigArgs = m_aJobCfg.getConfig(); + lJobConfigArgs = m_aJobCfg.getJobConfig(); + } + + aReadLock.unlock(); + /* } SAFE */ + + // Add all valid (not empty) lists to the return list + if (lConfigArgs.getLength()>0) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + lAllArgs[nLength].Name = ::rtl::OUString::createFromAscii(JobData::PROPSET_CONFIG); + lAllArgs[nLength].Value <<= lConfigArgs; + } + if (lJobConfigArgs.getLength()>0) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + lAllArgs[nLength].Name = ::rtl::OUString::createFromAscii(JobData::PROPSET_OWNCONFIG); + lAllArgs[nLength].Value <<= lJobConfigArgs; + } + if (lEnvArgs.getLength()>0) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + lAllArgs[nLength].Name = ::rtl::OUString::createFromAscii(JobData::PROPSET_ENVIRONMENT); + lAllArgs[nLength].Value <<= lEnvArgs; + } + if (lDynamicArgs.getLength()>0) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + lAllArgs[nLength].Name = ::rtl::OUString::createFromAscii(JobData::PROPSET_DYNAMICDATA); + lAllArgs[nLength].Value <<= lDynamicArgs; + } + + return lAllArgs; +} + +//________________________________ +/** + @short analyze the given job result and change the job configuration + @descr Note: Some results can be handled only, if this job has a valid configuration! + For "not configured jobs" (means pure services) they can be ignored. + But these cases are handled by our JobData member. We can call it everytime. + It does the right things automaticly. E.g. if the job has no configuration ... + it does nothing during setJobConfig()! + + @param aResult + the job result for analyzing +*/ +void Job::impl_reactForJobResult( /*IN*/ const css::uno::Any& aResult ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // analyze the result set ... + JobResult aAnalyzedResult(aResult); + + // some of the following operations will be supported for different environments + // or different type of jobs only. + JobData::EEnvironment eEnvironment = m_aJobCfg.getEnvironment(); + + // write back the job specific configuration data ... + // If the environment allow it and if this job has a configuration! + if ( + (m_aJobCfg.hasConfig() ) && + (aAnalyzedResult.existPart(JobResult::E_ARGUMENTS)) + ) + { + m_aJobCfg.setJobConfig(aAnalyzedResult.getArguments()); + } + + // disable a job for further executions. + // Note: this option is available inside the environment EXECUTOR only + if ( +// (eEnvironment == JobData::E_EXECUTION ) && + (m_aJobCfg.hasConfig() ) && + (aAnalyzedResult.existPart(JobResult::E_DEACTIVATE)) + ) + { + m_aJobCfg.disableJob(); + } + + // notify any interested listener with the may given result state. + // Note: this option is available inside the environment DISPATCH only + if ( + (eEnvironment == JobData::E_DISPATCH ) && + (m_xResultListener.is() ) && + (aAnalyzedResult.existPart(JobResult::E_DISPATCHRESULT)) + ) + { + m_aJobCfg.setResult(aAnalyzedResult); + // Attention: Because the listener expect that the original object send this event ... + // and we nor the job are the right ones ... + // our user has set itself before. So we can fake this source address! + css::frame::DispatchResultEvent aEvent = aAnalyzedResult.getDispatchResult(); + aEvent.Source = m_xResultSourceFake; + m_xResultListener->dispatchFinished(aEvent); + } + + aWriteLock.unlock(); + /* SAFE { */ +} + +//________________________________ +/** + @short starts listening for office shutdown and closing of our + given target frame (if its a valid reference) + @descr We will reghister ourself as terminate listener + at the global desktop instance. That will hold us + alive and additional we get the information, if the + office whish to shutdown. If then an internal job + is running we will have the chance to supress that + by throwing a veto exception. If our internal wrapped + job finished his work, we can release this listener + connection. + + Further we are listener for closing of the (possible valid) + given frame. We must be shure, that this ressource won't be gone + if our internal job is still running. +*/ +void Job::impl_startListening() +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // listening for office shutdown + if (!m_xDesktop.is() && !m_bListenOnDesktop) + { + try + { + m_xDesktop = css::uno::Reference< css::frame::XDesktop >(m_xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XTerminateListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + if (m_xDesktop.is()) + { + m_xDesktop->addTerminateListener(xThis); + m_bListenOnDesktop = sal_True; + } + } + catch(css::uno::Exception&) + { + m_xDesktop = css::uno::Reference< css::frame::XDesktop >(); + } + } + + // listening for frame closing + if (m_xFrame.is() && !m_bListenOnFrame) + { + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + if (xCloseable.is()) + { + xCloseable->addCloseListener(xThis); + m_bListenOnFrame = sal_True; + } + } + catch(css::uno::Exception&) + { + m_bListenOnFrame = sal_False; + } + } + + // listening for model closing + if (m_xModel.is() && !m_bListenOnModel) + { + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + if (xCloseable.is()) + { + xCloseable->addCloseListener(xThis); + m_bListenOnModel = sal_True; + } + } + catch(css::uno::Exception&) + { + m_bListenOnModel = sal_False; + } + } + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short release listener connection for office shutdown + @descr see description of impl_startListening() +*/ +void Job::impl_stopListening() +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // stop listening for office shutdown + if (m_xDesktop.is() && m_bListenOnDesktop) + { + try + { + css::uno::Reference< css::frame::XTerminateListener > xThis(static_cast< ::cppu::OWeakObject* >(this) , css::uno::UNO_QUERY); + m_xDesktop->removeTerminateListener(xThis); + m_xDesktop = css::uno::Reference< css::frame::XDesktop >(); + m_bListenOnDesktop = sal_False; + } + catch(css::uno::Exception&) + { + } + } + + // stop listening for frame closing + if (m_xFrame.is() && m_bListenOnFrame) + { + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + if (xCloseable.is()) + { + xCloseable->removeCloseListener(xThis); + m_bListenOnFrame = sal_False; + } + } + catch(css::uno::Exception&) + { + } + } + + // stop listening for model closing + if (m_xModel.is() && m_bListenOnModel) + { + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + if (xCloseable.is()) + { + xCloseable->removeCloseListener(xThis); + m_bListenOnModel = sal_False; + } + } + catch(css::uno::Exception&) + { + } + } + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short callback from any asynchronous executed job + + @descr Our execute() method waits for this callback. + We have to react for the possible results here, + to kill the running job and disable the blocked condition + so execute() can be finished too. + + @param xJob + the job, which was running and inform us now + + @param aResult + it's results +*/ +void SAL_CALL Job::jobFinished( /*IN*/ const css::uno::Reference< css::task::XAsyncJob >& xJob , + /*IN*/ const css::uno::Any& aResult ) throw(css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // It's neccessary to check this. + // May this job was cancelled by any other reason + // some milliseconds before. :-) + if (m_xJob.is() && m_xJob==xJob) + { + // react for his results + // (means enable/disable it for further requests + // or save arguments or notify listener ...) + impl_reactForJobResult(aResult); + + // Let the job die! + m_xJob = css::uno::Reference< css::uno::XInterface >(); + } + + // And let the start method "execute()" finishing it's job. + // But do it everytime. So any outside blocking code can finish + // his work too. + m_aAsyncWait.set(); + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short prevent internal wrapped job against office termination + @descr This event is broadcasted by the desktop instance and ask for an office termination. + If the internal wrapped job is still in progress, we disagree with that by throwing the + right veto exception. If not - we agree. But then we must be aware, that another event + notifyTermination() can follow. Then we have no chance to do the same. Then we have to + accept that and stop our work instandly. + + @param aEvent + describes the broadcaster and must be the desktop instance + + @throw TerminateVetoException + if our internal wrapped job is still running. + */ +void SAL_CALL Job::queryTermination( /*IN*/ const css::lang::EventObject& ) throw(css::frame::TerminationVetoException, + css::uno::RuntimeException ) +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + // don't disagree with this request if job was already stopped or finished it's work + // if (m_eRunState != E_RUNNING) + // return; + + // Otherwhise try to close() it + css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(sal_False); + m_eRunState = E_STOPPED_OR_FINISHED; + } + catch(const css::util::CloseVetoException&) {} + } + + if (m_eRunState != E_STOPPED_OR_FINISHED) + { + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + throw css::frame::TerminationVetoException(DECLARE_ASCII("job still in progress"), xThis); + } + + aReadLock.unlock(); + /* } SAFE */ +} + + +//________________________________ +/** + @short inform us about office termination + @descr Instead of the method queryTermination(), here is no chance to disagree with that. + We have to accept it and cancel all current processes inside. + It can occure only, if job was not already started if queryTermination() was called here .. + Then we had not throwed a veto exception. But now we must agree with this situation and break + all our internal processes. Its not a good idea to mark this instance as non startable any longer + inside queryTermination() if no job was unning too. Because that would disable this job and may + the office does not realy shutdownm, because another listener has thrown the suitable exception. + + @param aEvent + describes the broadcaster and must be the desktop instance + */ +void SAL_CALL Job::notifyTermination( /*IN*/ const css::lang::EventObject& ) throw(css::uno::RuntimeException) +{ + die(); + // Do nothing else here. Our internal ressources was released ... +} + +//________________________________ +/** + @short prevent internal wrapped job against frame closing + @descr This event is broadcasted by the frame instance and ask for closing. + If the internal wrapped job is still in progress, we disagree with that by throwing the + right veto exception. If not - we agree. But then we must be aware, that another event + notifyClosing() can follow. Then we have no chance to do the same. Then we have to + accept that and stop our work instandly. + + @param aEvent + describes the broadcaster and must be the frame instance + + @param bGetsOwnerShip + If it's set to <TRUE> and we throw the right veto excepion, we have to close this frame later + if our internal processes will be finished. If it's set to <FALSE/> we can ignore it. + + @throw CloseVetoException + if our internal wrapped job is still running. + */ +void SAL_CALL Job::queryClosing( const css::lang::EventObject& aEvent , + sal_Bool bGetsOwnership ) throw(css::util::CloseVetoException, + css::uno::RuntimeException ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // do nothing, if no internal job is still running ... + // The frame or model can be closed then successfully. + if (m_eRunState != E_RUNNING) + return; + + // try close() first at the job. + // The job can agree or disagree with this request. + css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY); + if (xClose.is()) + { + xClose->close(bGetsOwnership); + // Here we can say: "this job was stopped successfully". Because + // no veto exception was thrown! + m_eRunState = E_STOPPED_OR_FINISHED; + return; + } + + // try dispose() then + // Here the job has no chance for a veto. + // But we must be aware of an "already disposed exception"... + try + { + css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY); + if (xDispose.is()) + { + xDispose->dispose(); + m_eRunState = E_DISPOSED; + } + } + catch(const css::lang::DisposedException&) + { + // the job was already disposed by any other mechanism !? + // But it's not interesting for us. For us this job is stopped now. + m_eRunState = E_DISPOSED; + } + + if (m_eRunState != E_DISPOSED) + { + // analyze event source - to find out, which resource called queryClosing() at this + // job wrapper. We must bind a "pending close" request to this resource. + // Closing of the corresponding resource will be done if our internal job finish it's work. + m_bPendingCloseFrame = (m_xFrame.is() && aEvent.Source == m_xFrame); + m_bPendingCloseModel = (m_xModel.is() && aEvent.Source == m_xModel); + + // throw suitable veto exception - because the internal job could not be cancelled. + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + throw css::util::CloseVetoException(DECLARE_ASCII("job still in progress"), xThis); + } + + // No veto ... + // But don't call die() here or free our internal member. + // This must be done inside notifyClosing() only. Otherwhise the + // might stopped job has no chance to return it's results or + // call us back. We must give him the chance to finish it's work successfully. + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short inform us about frame closing + @descr Instead of the method queryClosing(), here is no chance to disagree with that. + We have to accept it and cancel all current processes inside. + + @param aEvent + describes the broadcaster and must be the frame or model instance we know + */ +void SAL_CALL Job::notifyClosing( const css::lang::EventObject& ) throw(css::uno::RuntimeException) +{ + die(); + // Do nothing else here. Our internal ressources was released ... +} + +//________________________________ +/** + @short shouldn't be called normaly + @descr But it doesn't matter, who called it. We have to kill our internal + running processes hardly. + + @param aEvent + describe the broadcaster +*/ +void SAL_CALL Job::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + if (m_xDesktop.is() && aEvent.Source == m_xDesktop) + { + m_xDesktop = css::uno::Reference< css::frame::XDesktop >(); + m_bListenOnDesktop = sal_False; + } + else + if (m_xFrame.is() && aEvent.Source == m_xFrame) + { + m_xFrame = css::uno::Reference< css::frame::XFrame >(); + m_bListenOnFrame = sal_False; + } + else + if (m_xModel.is() && aEvent.Source == m_xModel) + { + m_xModel = css::uno::Reference< css::frame::XModel >(); + m_bListenOnModel = sal_False; + } + + aWriteLock.unlock(); + /* } SAFE */ + + die(); + // Do nothing else here. Our internal ressources was released ... +} + +} // namespace framework diff --git a/framework/source/jobs/jobconst.cxx b/framework/source/jobs/jobconst.cxx new file mode 100644 index 000000000000..b71a68342a69 --- /dev/null +++ b/framework/source/jobs/jobconst.cxx @@ -0,0 +1,74 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes + +#include <jobs/jobconst.hxx> + +//________________________________ +// interface includes + +//________________________________ +// includes of other projects + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +const ::rtl::OUString JobConst::ANSWER_DEACTIVATE_JOB() +{ + static const ::rtl::OUString PROP = ::rtl::OUString::createFromAscii("Deactivate"); + return PROP; +} + +const ::rtl::OUString JobConst::ANSWER_SAVE_ARGUMENTS() +{ + static const ::rtl::OUString PROP = ::rtl::OUString::createFromAscii("SaveArguments"); + return PROP; +} + +const ::rtl::OUString JobConst::ANSWER_SEND_DISPATCHRESULT() +{ + static const ::rtl::OUString PROP = ::rtl::OUString::createFromAscii("SendDispatchResult"); + return PROP; +} + +} // namespace framework diff --git a/framework/source/jobs/jobdata.cxx b/framework/source/jobs/jobdata.cxx new file mode 100644 index 000000000000..4acbce44f831 --- /dev/null +++ b/framework/source/jobs/jobdata.cxx @@ -0,0 +1,714 @@ + /************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes +#include <jobs/jobdata.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <classes/converter.hxx> +#include <general.h> +#include <services.h> + +//________________________________ +// interface includes +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> + +//________________________________ +// includes of other projects +#include <tools/wldcrd.hxx> +#include <unotools/configpathes.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// exported const + +const sal_Char* JobData::JOBCFG_ROOT = "/org.openoffice.Office.Jobs/Jobs/" ; +const sal_Char* JobData::JOBCFG_PROP_SERVICE = "Service" ; +const sal_Char* JobData::JOBCFG_PROP_ARGUMENTS = "Arguments" ; + +const sal_Char* JobData::EVENTCFG_ROOT = "/org.openoffice.Office.Jobs/Events/" ; +const sal_Char* JobData::EVENTCFG_PATH_JOBLIST = "/JobList" ; +const sal_Char* JobData::EVENTCFG_PROP_ADMINTIME = "AdminTime" ; +const sal_Char* JobData::EVENTCFG_PROP_USERTIME = "UserTime" ; + +const sal_Char* JobData::PROPSET_CONFIG = "Config" ; +const sal_Char* JobData::PROPSET_OWNCONFIG = "JobConfig" ; +const sal_Char* JobData::PROPSET_ENVIRONMENT = "Environment" ; +const sal_Char* JobData::PROPSET_DYNAMICDATA = "DynamicData" ; + +const sal_Char* JobData::PROP_ALIAS = "Alias" ; +const sal_Char* JobData::PROP_EVENTNAME = "EventName" ; +const sal_Char* JobData::PROP_ENVTYPE = "EnvType" ; +const sal_Char* JobData::PROP_FRAME = "Frame" ; +const sal_Char* JobData::PROP_MODEL = "Model" ; +const sal_Char* JobData::PROP_SERVICE = "Service" ; + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +//________________________________ +/** + @short standard ctor + @descr It initialize this new instance. + But for real working it's neccessary to call setAlias() or setService() later. + Because we need the job data ... + + @param xSMGR + reference to the uno service manager +*/ +JobData::JobData( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + : ThreadHelpBase(&Application::GetSolarMutex()) + , m_xSMGR (xSMGR ) +{ + // share code for member initialization with defaults! + impl_reset(); +} + +//________________________________ +/** + @short copy ctor + @descr Sometimes such job data container must be moved from one using place + to another one. Then a copy ctor and copy operator must be available. + + @param rCopy + the original instance, from which we must copy all data +*/ +JobData::JobData( const JobData& rCopy ) + : ThreadHelpBase(&Application::GetSolarMutex()) +{ + // use the copy operator to share the same code + *this = rCopy; +} + +//________________________________ +/** + @short operator for coping JobData instances + @descr Sometimes such job data container must be moved from one using place + to another one. Then a copy ctor and copy operator must be available. + + @param rCopy + the original instance, from which we must copy all data +*/ +void JobData::operator=( const JobData& rCopy ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + // Please don't copy the uno service manager reference. + // That can change the uno context, which isn't a good idea! + m_eMode = rCopy.m_eMode ; + m_eEnvironment = rCopy.m_eEnvironment ; + m_sAlias = rCopy.m_sAlias ; + m_sService = rCopy.m_sService ; + m_sEvent = rCopy.m_sEvent ; + m_lArguments = rCopy.m_lArguments ; + m_aLastExecutionResult = rCopy.m_aLastExecutionResult; + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short let this instance die + @descr There is no chance any longer to work. We have to + release all used ressources and free used memory. +*/ +JobData::~JobData() +{ + impl_reset(); +} + +//________________________________ +/** + @short initalize this instance as a job with configuration + @descr They given alias can be used to adress some configuraton data. + We read it and fill our internal structures. Of course old informations + will be lost doing so. + + @param sAlias + the alias name of this job, used to locate job properties inside cfg +*/ +void JobData::setAlias( const ::rtl::OUString& sAlias ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + // delete all old informations! Otherwhise we mix it with the new one ... + impl_reset(); + + // take over the new informations + m_sAlias = sAlias; + m_eMode = E_ALIAS; + + // try to open the configuration set of this job directly and get a property access to it + // We open it readonly here + ::rtl::OUString sKey; + sKey = ::rtl::OUString::createFromAscii(JOBCFG_ROOT); + sKey += ::utl::wrapConfigurationElementName(m_sAlias); + + ConfigAccess aConfig(m_xSMGR, sKey); + aConfig.open(ConfigAccess::E_READONLY); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + { + impl_reset(); + return; + } + + css::uno::Reference< css::beans::XPropertySet > xJobProperties(aConfig.cfg(), css::uno::UNO_QUERY); + if (xJobProperties.is()) + { + css::uno::Any aValue; + + // read uno implementation name + aValue = xJobProperties->getPropertyValue(::rtl::OUString::createFromAscii(JOBCFG_PROP_SERVICE)); + aValue >>= m_sService; + + // read whole argument list + aValue = xJobProperties->getPropertyValue(::rtl::OUString::createFromAscii(JOBCFG_PROP_ARGUMENTS)); + css::uno::Reference< css::container::XNameAccess > xArgumentList; + if ( + (aValue >>= xArgumentList) && + (xArgumentList.is() ) + ) + { + css::uno::Sequence< ::rtl::OUString > lArgumentNames = xArgumentList->getElementNames(); + sal_Int32 nCount = lArgumentNames.getLength(); + m_lArguments.realloc(nCount); + for (sal_Int32 i=0; i<nCount; ++i) + { + m_lArguments[i].Name = lArgumentNames[i]; + m_lArguments[i].Value = xArgumentList->getByName(m_lArguments[i].Name); + } + } + } + + aConfig.close(); + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short initalize this instance as a job without configuration + @descr This job has no configuration data. We have to forget all old informations + and set only some of them new, so this instance can work. + + @param sService + the uno service name of this "non configured" job +*/ +void JobData::setService( const ::rtl::OUString& sService ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // delete all old informations! Otherwhise we mix it with the new one ... + impl_reset(); + // take over the new informations + m_sService = sService; + m_eMode = E_SERVICE; + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short initialize this instance with new job values. + @descr It reads automaticly all properties of the specified + job (using it's alias name) and "register it" for the + given event. This registration will not be validated against + the underlying configuration! (That must be done from outside. + Because the caller must have the configuration already open to + get the values for sEvent and sAlias! And doing so it can perform + only, if the time stanp values are readed outside too. + Further it make no sense to initialize and start a disabled job. + So this initialization method will be called for enabled jobs only.) + + @param sEvent + the triggered event, for which this job should be started + + @param sAlias + mark the required job inside event registration list +*/ +void JobData::setEvent( const ::rtl::OUString& sEvent , + const ::rtl::OUString& sAlias ) +{ + // share code to read all job properties! + setAlias(sAlias); + + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // take over the new informations - which differ against set on of method setAlias()! + m_sEvent = sEvent; + m_eMode = E_EVENT; + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short set the new job specific arguments + @descr If a job finish his work, it can give us a new list of arguments (which + will not interpreted by us). We write it back to the configuration only + (if this job has it's own configuration!). + So a job can have persistent data without implementing anything + or define own config areas for that. + + @param lArguments + list of arguments, which should be set for this job + */ +void JobData::setJobConfig( const css::uno::Sequence< css::beans::NamedValue >& lArguments ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // update member + m_lArguments = lArguments; + + // update the configuration ... if possible! + if (m_eMode==E_ALIAS) + { + // It doesn't matter if this config object was already opened before. + // It doesn nothing here then ... or it change the mode automaticly, if + // it was opened using another one before. + ::rtl::OUString sKey; + sKey = ::rtl::OUString::createFromAscii(JOBCFG_ROOT); + sKey += ::utl::wrapConfigurationElementName(m_sAlias); + + ConfigAccess aConfig(m_xSMGR, sKey); + aConfig.open(ConfigAccess::E_READWRITE); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + return; + + css::uno::Reference< css::beans::XMultiHierarchicalPropertySet > xArgumentList(aConfig.cfg(), css::uno::UNO_QUERY); + if (xArgumentList.is()) + { + sal_Int32 nCount = m_lArguments.getLength(); + css::uno::Sequence< ::rtl::OUString > lNames (nCount); + css::uno::Sequence< css::uno::Any > lValues(nCount); + + for (sal_Int32 i=0; i<nCount; ++i) + { + lNames [i] = m_lArguments[i].Name ; + lValues[i] = m_lArguments[i].Value; + } + + xArgumentList->setHierarchicalPropertyValues(lNames, lValues); + } + aConfig.close(); + } + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short set a new excution result + @descr Every executed job can have returned a result. + We set it here, so our user can use it may be later. + But the outside code can use it too, to analyze it and + adopt the configuration of this job too. Because the + result uses a protocol, which allow that. And we provide + right functionality to save it. + + @param aResult + the result of last execution + */ +void JobData::setResult( const JobResult& aResult ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // overwrite the last saved result + m_aLastExecutionResult = aResult; + + // Don't use his informations to update + // e.g. the arguments of this job. It must be done + // from outside! Here we save this information only. + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short set a new environment descriptor for this job + @descr It must(!) be done everytime this container is initialized + with new job datas e.g.: setAlias()/setEvent()/setService() ... + Otherwhise the environment will be unknown! + */ +void JobData::setEnvironment( EEnvironment eEnvironment ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + m_eEnvironment = eEnvironment; + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short these functions provides access to our internal members + @descr These member represent any information about the job + and can be used from outside to e.g. start a job. + */ +JobData::EMode JobData::getMode() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_eMode; + /* } SAFE */ +} + +//________________________________ + +JobData::EEnvironment JobData::getEnvironment() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_eEnvironment; + /* } SAFE */ +} + +//________________________________ + +::rtl::OUString JobData::getEnvironmentDescriptor() const +{ + ::rtl::OUString sDescriptor; + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + switch(m_eEnvironment) + { + case E_EXECUTION : + sDescriptor = ::rtl::OUString::createFromAscii("EXECUTOR"); + break; + + case E_DISPATCH : + sDescriptor = ::rtl::OUString::createFromAscii("DISPATCH"); + break; + + case E_DOCUMENTEVENT : + sDescriptor = ::rtl::OUString::createFromAscii("DOCUMENTEVENT"); + break; + default: + break; + } + /* } SAFE */ + return sDescriptor; +} + +//________________________________ + +::rtl::OUString JobData::getService() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_sService; + /* } SAFE */ +} + +//________________________________ + +::rtl::OUString JobData::getEvent() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_sEvent; + /* } SAFE */ +} + +//________________________________ + +css::uno::Sequence< css::beans::NamedValue > JobData::getJobConfig() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_lArguments; + /* } SAFE */ +} + +//________________________________ + +css::uno::Sequence< css::beans::NamedValue > JobData::getConfig() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + css::uno::Sequence< css::beans::NamedValue > lConfig; + if (m_eMode==E_ALIAS) + { + lConfig.realloc(2); + sal_Int32 i = 0; + + lConfig[i].Name = ::rtl::OUString::createFromAscii(PROP_ALIAS); + lConfig[i].Value <<= m_sAlias; + ++i; + + lConfig[i].Name = ::rtl::OUString::createFromAscii(PROP_SERVICE); + lConfig[i].Value <<= m_sService; + ++i; + } + aReadLock.unlock(); + /* } SAFE */ + return lConfig; +} + +//________________________________ +/** + @short return information, if this job is part of the global configuration package + org.openoffice.Office.Jobs + @descr Because jobs can be executed by the dispatch framework using an uno service name + directly - an executed job must not have any configuration realy. Such jobs + must provide the right interfaces only! But after finishing jobs can return + some informations (e.g. for updating her configuration ...). We must know + if such request is valid or not then. + + @return TRUE if the represented job is part of the underlying configuration package. + */ +sal_Bool JobData::hasConfig() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return (m_eMode==E_ALIAS || m_eMode==E_EVENT); + /* } SAFE */ +} + +//________________________________ +/** + @short mark a job as non startable for further requests + @descr We don't remove the configuration entry! We set a timestamp value only. + And there exist two of them: one for an administrator ... and one for the + current user. We change it for the user layer only. So this JobDispatch can't be + started any more ... till the administrator change his timestamp. + That can be usefull for post setup scenarios, which must run one time only. + + Note: This method don't do anything, if ths represented job doesn't have a configuration! + */ +void JobData::disableJob() +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // No configuration - not used from EXECUTOR and not triggered from an event => no chance! + if (m_eMode!=E_EVENT) + return; + + // update the configuration + // It doesn't matter if this config object was already opened before. + // It doesn nothing here then ... or it change the mode automaticly, if + // it was opened using another one before. + ::rtl::OUStringBuffer sKey(256); + sKey.appendAscii(JobData::EVENTCFG_ROOT ); + sKey.append (::utl::wrapConfigurationElementName(m_sEvent)); + sKey.appendAscii(JobData::EVENTCFG_PATH_JOBLIST ); + sKey.appendAscii("/" ); + sKey.append (::utl::wrapConfigurationElementName(m_sAlias)); + + ConfigAccess aConfig(m_xSMGR, sKey.makeStringAndClear()); + aConfig.open(ConfigAccess::E_READWRITE); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + return; + + css::uno::Reference< css::beans::XPropertySet > xPropSet(aConfig.cfg(), css::uno::UNO_QUERY); + if (xPropSet.is()) + { + // Convert and write the user timestamp to the configuration. + css::uno::Any aValue; + aValue <<= Converter::convert_DateTime2ISO8601(DateTime()); + xPropSet->setPropertyValue(::rtl::OUString::createFromAscii(EVENTCFG_PROP_USERTIME), aValue); + } + + aConfig.close(); + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + */ +sal_Bool isEnabled( const ::rtl::OUString& sAdminTime , + const ::rtl::OUString& sUserTime ) +{ + /*Attention! + To prevent interpreting of TriGraphs inside next const string value, + we have to encode all '?' signs. Otherwhise e.g. "??-" will be translated + to "~" ... + */ + static ::rtl::OUString PATTERN_ISO8601 = ::rtl::OUString::createFromAscii("\?\?\?\?-\?\?-\?\?*\0"); + WildCard aISOPattern(PATTERN_ISO8601); + + sal_Bool bValidAdmin = aISOPattern.Matches(sAdminTime); + sal_Bool bValidUser = aISOPattern.Matches(sUserTime ); + + // We check for "isEnabled()" here only. + // Note further: ISO8601 formated strings can be compared as strings directly! + return ( + (!bValidAdmin && !bValidUser ) || + ( bValidAdmin && bValidUser && sAdminTime>=sUserTime) + ); +} + +//________________________________ +/** + */ +void JobData::appendEnabledJobsForEvent( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR , + const ::rtl::OUString& sEvent , + ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding >& lJobs ) +{ + css::uno::Sequence< ::rtl::OUString > lAdditionalJobs = JobData::getEnabledJobsForEvent(xSMGR, sEvent); + sal_Int32 c = lAdditionalJobs.getLength(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + JobData::TJob2DocEventBinding aBinding(lAdditionalJobs[i], sEvent); + lJobs.push_back(aBinding); + } +} + +//________________________________ +/** + */ +css::uno::Sequence< ::rtl::OUString > JobData::getEnabledJobsForEvent( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR , + const ::rtl::OUString& sEvent ) +{ + // these static values may perform following loop for reading time stamp values ... + static ::rtl::OUString ADMINTIME = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_PROP_ADMINTIME); + static ::rtl::OUString USERTIME = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_PROP_USERTIME ); + static ::rtl::OUString ROOT = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_ROOT ); + static ::rtl::OUString JOBLIST = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_PATH_JOBLIST ); + + // create a config access to "/org.openoffice.Office.Jobs/Events" + ConfigAccess aConfig(xSMGR,ROOT); + aConfig.open(ConfigAccess::E_READONLY); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + return css::uno::Sequence< ::rtl::OUString >(); + + css::uno::Reference< css::container::XHierarchicalNameAccess > xEventRegistry(aConfig.cfg(), css::uno::UNO_QUERY); + if (!xEventRegistry.is()) + return css::uno::Sequence< ::rtl::OUString >(); + + // check if the given event exist inside list of registered ones + ::rtl::OUString sPath(sEvent); + sPath += JOBLIST; + if (!xEventRegistry->hasByHierarchicalName(sPath)) + return css::uno::Sequence< ::rtl::OUString >(); + + // step to the job list, which is a child of the event node inside cfg + // e.g. "/org.openoffice.Office.Jobs/Events/<event name>/JobList" + css::uno::Any aJobList = xEventRegistry->getByHierarchicalName(sPath); + css::uno::Reference< css::container::XNameAccess > xJobList; + if (!(aJobList >>= xJobList) || !xJobList.is()) + return css::uno::Sequence< ::rtl::OUString >(); + + // get all alias names of jobs, which are part of this job list + // But Some of them can be disabled by it's time stamp values. + // We create an additional job name list iwth the same size, then the original list ... + // step over all job entries ... check her time stamps ... and put only job names to the + // destination list, which represent an enabled job. + css::uno::Sequence< ::rtl::OUString > lAllJobs = xJobList->getElementNames(); + ::rtl::OUString* pAllJobs = lAllJobs.getArray(); + sal_Int32 c = lAllJobs.getLength(); + + css::uno::Sequence< ::rtl::OUString > lEnabledJobs(c); + ::rtl::OUString* pEnabledJobs = lEnabledJobs.getArray(); + sal_Int32 d = 0; + + for (sal_Int32 s=0; s<c; ++s) + { + css::uno::Reference< css::beans::XPropertySet > xJob; + if ( + !(xJobList->getByName(pAllJobs[s]) >>= xJob) || + !(xJob.is() ) + ) + { + continue; + } + + ::rtl::OUString sAdminTime; + xJob->getPropertyValue(ADMINTIME) >>= sAdminTime; + + ::rtl::OUString sUserTime; + xJob->getPropertyValue(USERTIME) >>= sUserTime; + + if (!isEnabled(sAdminTime, sUserTime)) + continue; + + pEnabledJobs[d] = pAllJobs[s]; + ++d; + } + lEnabledJobs.realloc(d); + + aConfig.close(); + + return lEnabledJobs; +} + +//________________________________ +/** + @short reset all internal structures + @descr If somehwere recycle this instance, he can switch from one + using mode to another one. But then we have to reset all currently + used informations. Otherwhise we mix it and they can make trouble. + + But note: that does not set defaults for internal used members, which + does not relate to any job property! e.g. the reference to the global + uno service manager. Such informations are used for internal processes only + and are neccessary for our work. + */ +void JobData::impl_reset() +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + m_eMode = E_UNKNOWN_MODE; + m_eEnvironment = E_UNKNOWN_ENVIRONMENT; + m_sAlias = ::rtl::OUString(); + m_sService = ::rtl::OUString(); + m_sEvent = ::rtl::OUString(); + m_lArguments = css::uno::Sequence< css::beans::NamedValue >(); + aWriteLock.unlock(); + /* } SAFE */ +} + +} // namespace framework diff --git a/framework/source/jobs/jobdispatch.cxx b/framework/source/jobs/jobdispatch.cxx new file mode 100644 index 000000000000..b2c5399d0216 --- /dev/null +++ b/framework/source/jobs/jobdispatch.cxx @@ -0,0 +1,475 @@ + /************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes +#include <jobs/jobdispatch.hxx> +#include <jobs/joburl.hxx> +#include <jobs/job.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <threadhelp/resetableguard.hxx> +#include <classes/converter.hxx> +#include <general.h> +#include <services.h> + +//________________________________ +// interface includes +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> + +//________________________________ +// includes of other projects +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +DEFINE_XINTERFACE_6( JobDispatch , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::frame::XDispatchProvider ), + DIRECT_INTERFACE(css::lang::XInitialization ), + DIRECT_INTERFACE(css::lang::XServiceInfo), + DIRECT_INTERFACE(css::frame::XNotifyingDispatch), + DIRECT_INTERFACE(css::frame::XDispatch ) + ) + +DEFINE_XTYPEPROVIDER_6( JobDispatch , + css::lang::XTypeProvider , + css::frame::XDispatchProvider , + css::frame::XNotifyingDispatch, + css::lang::XInitialization, + css::lang::XServiceInfo, + css::frame::XDispatch + ) + +DEFINE_XSERVICEINFO_MULTISERVICE( JobDispatch , + ::cppu::OWeakObject , + SERVICENAME_PROTOCOLHANDLER , + IMPLEMENTATIONNAME_JOBDISPATCH + ) + +DEFINE_INIT_SERVICE( JobDispatch, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + } + ) + +//________________________________ +/** + @short standard ctor + @descr It initialize this new instance. + + @param xSMGR + reference to the uno service manager +*/ +JobDispatch::JobDispatch( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + : ThreadHelpBase(&Application::GetSolarMutex()) + , OWeakObject ( ) + , m_xSMGR (xSMGR ) +{ +} + +//________________________________ +/** + @short let this instance die + @descr We have to release all used ressources and free used memory. +*/ +JobDispatch::~JobDispatch() +{ + // release all used ressources + m_xSMGR = css::uno::Reference< css::lang::XMultiServiceFactory >(); + m_xFrame = css::uno::Reference< css::frame::XFrame >(); +} + +//________________________________ +/** + @short implementation of XInitalization + @descr A protocol handler can provide this functionality, if it wish to get additional informations + about the context it runs. In this case the frame reference would be given by the outside code. + + @param lArguments + the list of initialization arguments + First parameter should be the frame reference we need. +*/ +void SAL_CALL JobDispatch::initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) throw(css::uno::Exception , + css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + for (int a=0; a<lArguments.getLength(); ++a) + { + if (a==0) + lArguments[a] >>= m_xFrame; + } + + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short implementation of XDispatchProvider::queryDispatches() + @descr Every protocol handler will be asked for his agreement, if an URL was queried + for which this handler is registered. It's the chance for this handler to validate + the given URL and return a dispatch object (may be itself) or not. + + @param aURL + the queried URL, which should be checked + + @param sTargetFrameName + describes the target frame, in which context this handler will be used + Is mostly set to "", "_self", "_blank", "_default" or a non special one + using SELF/CREATE as search flags. + + @param nSearchFlags + Can be SELF or CREATE only and are set only if sTargetFrameName isn't a special target +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL JobDispatch::queryDispatch( /*IN*/ const css::util::URL& aURL , + /*IN*/ const ::rtl::OUString& /*sTargetFrameName*/ , + /*IN*/ sal_Int32 /*nSearchFlags*/ ) throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::frame::XDispatch > xDispatch; + + JobURL aAnalyzedURL(aURL.Complete); + if (aAnalyzedURL.isValid()) + xDispatch = css::uno::Reference< css::frame::XDispatch >( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + return xDispatch; +} + +//________________________________ +/** + @short implementation of XDispatchProvider::queryDispatches() + @descr It's an optimized access for remote, so you can ask for + multiple dispatch objects at the same time. + + @param lDescriptor + a list of queryDispatch() parameter + + @return A list of corresponding dispatch objects. + NULL references are not skipped. Every result + match to one given descriptor item. +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL JobDispatch::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) throw(css::uno::RuntimeException) +{ + // don't pack resulting list! + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches(nCount); + + for (sal_Int32 i=0; i<nCount; ++i) + lDispatches[i] = queryDispatch( lDescriptor[i].FeatureURL , + lDescriptor[i].FrameName , + lDescriptor[i].SearchFlags ); + return lDispatches; +} + +//________________________________ +/** + @short implementation of XNotifyingDispatch::dispatchWithNotification() + @descr It creates the job service implementation and call execute on it. + Further it starts the life time control of it. (important for async job) + For synchonrous job we react for the returned result directly ... for asynchronous + ones we do it later inside our callback method. But we use the same impl method + doing that to share the code. (see impl_finishJob()) + + If a job is already running, (it can only occure for asynchronous jobs) + don't start the same job a second time. Queue in the given dispatch parameter + and return immediatly. If the current running job call us back, we will start this + new dispatch request. + If no job is running - queue the parameter too! But then start the new job immediatly. + We have to queue it every time - because it hold us alive by ref count! + + @param aURL + describe the job(s), which should be started + + @param lArgs + optional arguments for this request + + @param xListener + an interested listener for possible results of this operation +*/ +void SAL_CALL JobDispatch::dispatchWithNotification( /*IN*/ const css::util::URL& aURL , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) throw(css::uno::RuntimeException) +{ + JobURL aAnalyzedURL(aURL.Complete); + if (aAnalyzedURL.isValid()) + { + ::rtl::OUString sRequest; + if (aAnalyzedURL.getEvent(sRequest)) + impl_dispatchEvent(sRequest, lArgs, xListener); + else + if (aAnalyzedURL.getService(sRequest)) + impl_dispatchService(sRequest, lArgs, xListener); + else + if (aAnalyzedURL.getAlias(sRequest)) + impl_dispatchAlias(sRequest, lArgs, xListener); + } +} + +//________________________________ +/** + @short dispatch an event + @descr We search all registered jobs for this event and execute it. + After doing so, we inform the given listener about the results. + (There will be one notify for every executed job!) + + @param sEvent + the event, for which jobs can be registered + + @param lArgs + optional arguments for this request + Currently not used! + + @param xListener + an interested listener for possible results of this operation +*/ +void JobDispatch::impl_dispatchEvent( /*IN*/ const ::rtl::OUString& sEvent , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // get list of all enabled jobs + // The called static helper methods read it from the configuration and + // filter disabled jobs using it's time stamp values. + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + css::uno::Sequence< ::rtl::OUString > lJobs = JobData::getEnabledJobsForEvent(m_xSMGR, sEvent); + aReadLock.unlock(); + /* } SAFE */ + + css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + // no jobs ... no execution + // But a may given listener will know something ... + // I think this operaton was finished successfully. + // It's not realy an error, if no registered jobs could be located. + if (lJobs.getLength()<1 && xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + aEvent.Source = xThis; + aEvent.State = css::frame::DispatchResultState::SUCCESS; + xListener->dispatchFinished(aEvent); + return; + } + + // Step over all found jobs and execute it + for (int j=0; j<lJobs.getLength(); ++j) + { + /* SAFE { */ + aReadLock.lock(); + + JobData aCfg(m_xSMGR); + aCfg.setEvent(sEvent, lJobs[j]); + aCfg.setEnvironment(JobData::E_DISPATCH); + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + Job* pJob = new Job(m_xSMGR, m_xFrame); + css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); + pJob->setJobData(aCfg); + + aReadLock.unlock(); + /* } SAFE */ + + // Special mode for listener. + // We dont notify it directly here. We delegate that + // to the job implementation. But we must set ourself there too. + // Because this job must fake the source adress of the event. + // Otherwhise the listener may will ignore it. + if (xListener.is()) + pJob->setDispatchResultFake(xListener, xThis); + pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs)); + } +} + +//________________________________ +/** + @short dispatch a service + @descr We use the given name only to create and if possible to initialize + it as an uno service. It can be usefully for creating (caching?) + of e.g. one instance services. + + @param sService + the uno implementation or service name of the job, which should be instanciated + + @param lArgs + optional arguments for this request + Currently not used! + + @param xListener + an interested listener for possible results of this operation +*/ +void JobDispatch::impl_dispatchService( /*IN*/ const ::rtl::OUString& sService , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + JobData aCfg(m_xSMGR); + aCfg.setService(sService); + aCfg.setEnvironment(JobData::E_DISPATCH); + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + Job* pJob = new Job(m_xSMGR, m_xFrame); + css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); + pJob->setJobData(aCfg); + + aReadLock.unlock(); + /* } SAFE */ + + css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + // Special mode for listener. + // We dont notify it directly here. We delegate that + // to the job implementation. But we must set ourself there too. + // Because this job must fake the source adress of the event. + // Otherwhise the listener may will ignore it. + if (xListener.is()) + pJob->setDispatchResultFake(xListener, xThis); + pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs)); +} + +//________________________________ +/** + @short dispatch an alias + @descr We use this alias to locate a job inside the configuration + and execute it. Further we inform the given listener about the results. + + @param sAlias + the alias name of the configured job + + @param lArgs + optional arguments for this request + Currently not used! + + @param xListener + an interested listener for possible results of this operation +*/ +void JobDispatch::impl_dispatchAlias( /*IN*/ const ::rtl::OUString& sAlias , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + JobData aCfg(m_xSMGR); + aCfg.setAlias(sAlias); + aCfg.setEnvironment(JobData::E_DISPATCH); + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + Job* pJob = new Job(m_xSMGR, m_xFrame); + css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); + pJob->setJobData(aCfg); + + aReadLock.unlock(); + /* } SAFE */ + + css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + // Special mode for listener. + // We dont notify it directly here. We delegate that + // to the job implementation. But we must set ourself there too. + // Because this job must fake the source adress of the event. + // Otherwhise the listener may will ignore it. + if (xListener.is()) + pJob->setDispatchResultFake(xListener, xThis); + pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs)); +} + +//________________________________ +/** + @short implementation of XDispatch::dispatch() + @descr Because the methods dispatch() and dispatchWithNotification() are different in her parameters + only, we can forward this request to dispatchWithNotification() by using an empty listener! + + @param aURL + describe the job(s), which should be started + + @param lArgs + optional arguments for this request + + @see dispatchWithNotification() +*/ +void SAL_CALL JobDispatch::dispatch( /*IN*/ const css::util::URL& aURL , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) throw(css::uno::RuntimeException) +{ + dispatchWithNotification(aURL, lArgs, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +//________________________________ +/** + @short not supported +*/ +void SAL_CALL JobDispatch::addStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&, + /*IN*/ const css::util::URL& ) throw(css::uno::RuntimeException) +{ +} + +//________________________________ +/** + @short not supported +*/ +void SAL_CALL JobDispatch::removeStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&, + /*IN*/ const css::util::URL& ) throw(css::uno::RuntimeException) +{ +} + +} // namespace framework diff --git a/framework/source/jobs/jobexecutor.cxx b/framework/source/jobs/jobexecutor.cxx new file mode 100644 index 000000000000..e3a6122d4938 --- /dev/null +++ b/framework/source/jobs/jobexecutor.cxx @@ -0,0 +1,364 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes +#include <jobs/jobexecutor.hxx> +#include <jobs/job.hxx> +#include <jobs/joburl.hxx> + +#ifndef __FRAMEWORK_CLASS_CONVERTER_HXX_ +#include <classes/converter.hxx> +#endif +#include <threadhelp/transactionguard.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <general.h> +#include <services.h> + +//________________________________ +// interface includes +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XContainer.hpp> + +//________________________________ +// includes of other projects +#include <unotools/configpathes.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +#include <rtl/logfile.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +DEFINE_XINTERFACE_6( JobExecutor , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::lang::XServiceInfo ), + DIRECT_INTERFACE(css::task::XJobExecutor ), + DIRECT_INTERFACE(css::container::XContainerListener ), + DIRECT_INTERFACE(css::document::XEventListener ), + DERIVED_INTERFACE(css::lang::XEventListener,css::document::XEventListener) + ) + +DEFINE_XTYPEPROVIDER_6( JobExecutor , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::task::XJobExecutor , + css::container::XContainerListener, + css::document::XEventListener , + css::lang::XEventListener + ) + +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE( JobExecutor , + ::cppu::OWeakObject , + SERVICENAME_JOBEXECUTOR , + IMPLEMENTATIONNAME_JOBEXECUTOR + ) + +DEFINE_INIT_SERVICE( JobExecutor, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + // read the list of all currently registered events inside configuration. + // e.g. "/org.openoffice.Office.Jobs/Events/<event name>" + // We need it later to check if an incoming event request can be executed successfully + // or must be rejected. It's an optimization! Of course we must implement updating of this + // list too ... Be listener at the configuration. + + m_aConfig.open(ConfigAccess::E_READONLY); + if (m_aConfig.getMode() == ConfigAccess::E_READONLY) + { + css::uno::Reference< css::container::XNameAccess > xRegistry(m_aConfig.cfg(), css::uno::UNO_QUERY); + if (xRegistry.is()) + m_lEvents = Converter::convert_seqOUString2OUStringList(xRegistry->getElementNames()); + + css::uno::Reference< css::container::XContainer > xNotifier(m_aConfig.cfg(), css::uno::UNO_QUERY); + if (xNotifier.is()) + { + css::uno::Reference< css::container::XContainerListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + xNotifier->addContainerListener(xThis); + } + + // don't close cfg here! + // It will be done inside disposing ... + } + } + ) + +//________________________________ + +/** + @short standard ctor + @descr It initialize this new instance. + + @param xSMGR + reference to the uno service manager + */ +JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + : ThreadHelpBase (&Application::GetSolarMutex() ) + , ::cppu::OWeakObject ( ) + , m_xSMGR (xSMGR ) + , m_aConfig (xSMGR, ::rtl::OUString::createFromAscii(JobData::EVENTCFG_ROOT) ) +{ + // Don't do any reference related code here! Do it inside special + // impl_ method() ... see DEFINE_INIT_SERVICE() macro for further informations. +} + +JobExecutor::~JobExecutor() +{ + LOG_ASSERT(m_aConfig.getMode() == ConfigAccess::E_CLOSED, "JobExecutor::~JobExecutor()\nConfiguration don't send dispoing() message!\n") +} + +//________________________________ + +/** + @short implementation of XJobExecutor interface + @descr We use the given event to locate any registered job inside our configuration + and execute it. Further we control the lifetime of it and supress + shutdown of the office till all jobs was finished. + + @param sEvent + is used to locate registered jobs + */ +void SAL_CALL JobExecutor::trigger( const ::rtl::OUString& sEvent ) throw(css::uno::RuntimeException) +{ + RTL_LOGFILE_CONTEXT(aLog, "fwk (as96863) JobExecutor::trigger()"); + + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + // Optimization! + // Check if the given event name exist inside configuration and reject wrong requests. + // This optimization supress using of the cfg api for getting event and job descriptions ... + if (m_lEvents.find(sEvent) == m_lEvents.end()) + return; + + // get list of all enabled jobs + // The called static helper methods read it from the configuration and + // filter disabled jobs using it's time stamp values. + css::uno::Sequence< ::rtl::OUString > lJobs = JobData::getEnabledJobsForEvent(m_xSMGR, sEvent); + + aReadLock.unlock(); + /* } SAFE */ + + // step over all enabled jobs and execute it + sal_Int32 c = lJobs.getLength(); + for (sal_Int32 j=0; j<c; ++j) + { + /* SAFE { */ + aReadLock.lock(); + + JobData aCfg(m_xSMGR); + aCfg.setEvent(sEvent, lJobs[j]); + aCfg.setEnvironment(JobData::E_EXECUTION); + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + Job* pJob = new Job(m_xSMGR, css::uno::Reference< css::frame::XFrame >()); + css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); + pJob->setJobData(aCfg); + + aReadLock.unlock(); + /* } SAFE */ + + pJob->execute(css::uno::Sequence< css::beans::NamedValue >()); + } +} + +//________________________________ + +void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent ) throw(css::uno::RuntimeException) +{ + static ::rtl::OUString EVENT_ON_NEW = DECLARE_ASCII("OnNew" ); // Doc UI event + static ::rtl::OUString EVENT_ON_LOAD = DECLARE_ASCII("OnLoad" ); // Doc UI event + static ::rtl::OUString EVENT_ON_CREATE = DECLARE_ASCII("OnCreate" ); // Doc API event + static ::rtl::OUString EVENT_ON_LOAD_FINISHED = DECLARE_ASCII("OnLoadFinished" ); // Doc API event + static ::rtl::OUString EVENT_ON_DOCUMENT_OPENED = DECLARE_ASCII("onDocumentOpened" ); // Job UI event : OnNew or OnLoad + static ::rtl::OUString EVENT_ON_DOCUMENT_ADDED = DECLARE_ASCII("onDocumentAdded" ); // Job API event : OnCreate or OnLoadFinished + + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding > lJobs; + + // Optimization! + // Check if the given event name exist inside configuration and reject wrong requests. + // This optimization supress using of the cfg api for getting event and job descriptions. + // see using of m_lEvents.find() below ... + + // Special feature: If the events "OnNew" or "OnLoad" occures - we generate our own event "onDocumentOpened". + if ( + (aEvent.EventName.equals(EVENT_ON_NEW )) || + (aEvent.EventName.equals(EVENT_ON_LOAD)) + ) + { + if (m_lEvents.find(EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end()) + JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_OPENED, lJobs); + } + + // Special feature: If the events "OnCreate" or "OnLoadFinished" occures - we generate our own event "onDocumentAdded". + if ( + (aEvent.EventName.equals(EVENT_ON_CREATE )) || + (aEvent.EventName.equals(EVENT_ON_LOAD_FINISHED)) + ) + { + if (m_lEvents.find(EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end()) + JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_ADDED, lJobs); + } + + // Add all jobs for "real" notified event too .-) + if (m_lEvents.find(aEvent.EventName) != m_lEvents.end()) + JobData::appendEnabledJobsForEvent(m_xSMGR, aEvent.EventName, lJobs); + + aReadLock.unlock(); + /* } SAFE */ + + // step over all enabled jobs and execute it + ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding >::const_iterator pIt; + for ( pIt = lJobs.begin(); + pIt != lJobs.end() ; + ++pIt ) + { + /* SAFE { */ + aReadLock.lock(); + + const JobData::TJob2DocEventBinding& rBinding = *pIt; + + JobData aCfg(m_xSMGR); + aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName); + aCfg.setEnvironment(JobData::E_DOCUMENTEVENT); + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY); + Job* pJob = new Job(m_xSMGR, xModel); + css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); + pJob->setJobData(aCfg); + + aReadLock.unlock(); + /* } SAFE */ + + pJob->execute(css::uno::Sequence< css::beans::NamedValue >()); + } +} + +//________________________________ + +void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException) +{ + ::rtl::OUString sValue; + if (aEvent.Accessor >>= sValue) + { + ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue); + if (sEvent.getLength() > 0) + { + OUStringList::iterator pEvent = m_lEvents.find(sEvent); + if (pEvent == m_lEvents.end()) + m_lEvents.push_back(sEvent); + } + } +} + +void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException) +{ + ::rtl::OUString sValue; + if (aEvent.Accessor >>= sValue) + { + ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue); + if (sEvent.getLength() > 0) + { + OUStringList::iterator pEvent = m_lEvents.find(sEvent); + if (pEvent != m_lEvents.end()) + m_lEvents.erase(pEvent); + } + } +} + +void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& ) throw(css::uno::RuntimeException) +{ + // I'm not interested on changed items :-) +} + +//________________________________ + +/** @short the used cfg changes notifier wish to be released in its reference. + + @descr We close our internal used configuration instance to + free this reference. + + @attention For the special feature "bind global document event broadcaster to job execution" + this job executor instance was registered from outside code as + css.document.XEventListener. So it can be, that this disposing call comes from + the global event broadcaster service. But we don't hold any reference to this service + which can or must be released. Because this broadcaster itself is an one instance service + too, we can ignore this request. On the other side we must relase our internal CFG + reference ... SOLUTION => check the given event source and react only, if it's our internal + hold configuration object! + */ +void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException) +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY); + if ( + (xCFG == aEvent.Source ) && + (m_aConfig.getMode() != ConfigAccess::E_CLOSED) + ) + { + m_aConfig.close(); + } + aReadLock.unlock(); + /* } SAFE */ +} + +} // namespace framework diff --git a/framework/source/jobs/jobresult.cxx b/framework/source/jobs/jobresult.cxx new file mode 100644 index 000000000000..375a3e5fc4d6 --- /dev/null +++ b/framework/source/jobs/jobresult.cxx @@ -0,0 +1,261 @@ + /************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes + +#include <jobs/jobresult.hxx> +#include <jobs/jobconst.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <general.h> +#include <services.h> + +//________________________________ +// interface includes + +//________________________________ +// includes of other projects + +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/sequenceashashmap.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +//________________________________ +/** + @short standard dtor + @descr It does nothing else ... + but it marks this new instance as non valid! +*/ +JobResult::JobResult() + : ThreadHelpBase(&Application::GetSolarMutex()) +{ + // reset the flag mask! + // It will reset the accessible state of this object. + // That can be usefull if something will fail here ... + m_eParts = E_NOPART; +} + +//________________________________ +/** + @short special ctor + @descr It initialize this new instance with a pure job execution result + and analyze it. Doing so, we update our other members. + + <p> + It's a list of named values, packed inside this any. + Following protocol is used: + <p> + <ul> + <li> + "SaveArguments" [sequence< css.beans.NamedValue >] + <br> + The returned list of (for this generic implementation unknown!) + properties, will be written directly to the configuration and replace + any old values there. There will no check for changes and we doesn't + support any mege feature here. They are written only. The job has + to modify this list. + </li> + <li> + "SendDispatchResult" [css.frame.DispatchResultEvent] + <br> + The given event is send to all current registered listener. + But it's not guaranteed. In case no listener are available or + this job isn't part of the dispatch environment (because it was used + by the css..task.XJobExecutor->trigger() implementation) this option + will be ignored. + </li> + <li> + "Deactivate" [boolean] + <br> + The job whish to be disabled. But note: There is no way, to enable it later + again by using this implementation. It can be done by using the configuration + only. (Means to register this job again.) + If a job knows, that there exist some status or result listener, it must use + the options "SendDispatchStatus" and "SendDispatchResult" (see before) too, to + inform it about the deactivation of this service. + </li> + </ul> + + @param aResult + the job result +*/ +JobResult::JobResult( /*IN*/ const css::uno::Any& aResult ) + : ThreadHelpBase(&Application::GetSolarMutex()) +{ + // safe the pure result + // May someone need it later ... + m_aPureResult = aResult; + + // reset the flag mask! + // It will reset the accessible state of this object. + // That can be usefull if something will fail here ... + m_eParts = E_NOPART; + + // analyze the result and update our other members + ::comphelper::SequenceAsHashMap aProtocol(aResult); + if ( aProtocol.empty() ) + return; + + ::comphelper::SequenceAsHashMap::const_iterator pIt = aProtocol.end(); + + pIt = aProtocol.find(JobConst::ANSWER_DEACTIVATE_JOB()); + if (pIt != aProtocol.end()) + { + pIt->second >>= m_bDeactivate; + if (m_bDeactivate) + m_eParts |= E_DEACTIVATE; + } + + pIt = aProtocol.find(JobConst::ANSWER_SAVE_ARGUMENTS()); + if (pIt != aProtocol.end()) + { + pIt->second >>= m_lArguments; + if (m_lArguments.getLength() > 0) + m_eParts |= E_ARGUMENTS; + } + + pIt = aProtocol.find(JobConst::ANSWER_SEND_DISPATCHRESULT()); + if (pIt != aProtocol.end()) + { + if (pIt->second >>= m_aDispatchResult) + m_eParts |= E_DISPATCHRESULT; + } +} + +//________________________________ +/** + @short copy dtor + @descr - +*/ +JobResult::JobResult( const JobResult& rCopy ) + : ThreadHelpBase(&Application::GetSolarMutex()) +{ + m_aPureResult = rCopy.m_aPureResult ; + m_eParts = rCopy.m_eParts ; + m_lArguments = rCopy.m_lArguments ; + m_bDeactivate = rCopy.m_bDeactivate ; + m_aDispatchResult = rCopy.m_aDispatchResult ; +} + +//________________________________ +/** + @short standard dtor + @descr Free all internaly used ressources at the end of living. +*/ +JobResult::~JobResult() +{ + // Nothing realy to do here. +} + +//________________________________ +/** + @short =operator + @descr Must be implemented to overwrite this instance with another one. + + @param rCopy + reference to the other instance, which should be used for copying. +*/ +void JobResult::operator=( const JobResult& rCopy ) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + m_aPureResult = rCopy.m_aPureResult ; + m_eParts = rCopy.m_eParts ; + m_lArguments = rCopy.m_lArguments ; + m_bDeactivate = rCopy.m_bDeactivate ; + m_aDispatchResult = rCopy.m_aDispatchResult ; + aWriteLock.unlock(); + /* } SAFE */ +} + +//________________________________ +/** + @short checks for existing parts of the analyzed result + @descr The internal flag mask was set after analyzing of the pure result. + An user of us can check here, if the required part was realy part + of this result. Otherwhise it would use invalid informations ... + by using our other members! + + @param eParts + a flag mask too, which will be compared with our internaly set one. + + @return We return true only, if any set flag of the given mask match. +*/ +sal_Bool JobResult::existPart( sal_uInt32 eParts ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return ((m_eParts & eParts) == eParts); + /* } SAFE */ +} + +//________________________________ +/** + @short provides access to our internal members + @descr The return value will be valid only in case a call of + existPart(E_...) before returned true! + + @return It returns the state of the internal member + without any checks! +*/ +css::uno::Sequence< css::beans::NamedValue > JobResult::getArguments() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_lArguments; + /* } SAFE */ +} + +//________________________________ + +css::frame::DispatchResultEvent JobResult::getDispatchResult() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_aDispatchResult; + /* } SAFE */ +} + +} // namespace framework diff --git a/framework/source/jobs/joburl.cxx b/framework/source/jobs/joburl.cxx new file mode 100644 index 000000000000..59e6384a0e93 --- /dev/null +++ b/framework/source/jobs/joburl.cxx @@ -0,0 +1,657 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//________________________________ +// my own includes +#include <jobs/joburl.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <general.h> + +//________________________________ +// interface includes + +//________________________________ +// includes of other projects +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +//________________________________ +// namespace + +namespace framework{ + +//________________________________ +// non exported const + +//________________________________ +// non exported definitions + +//________________________________ +// declarations + +//________________________________ +/** + @short special ctor + @descr It initialize this new instance with a (hopyfully) valid job URL. + This URL will be parsed. After that we set our members right, + so other interface methods of this class can be used to get + all items of this URL. Of course it will be possible to know, + if this URL was valid too. + + @param sURL + the job URL for parsing +*/ +JobURL::JobURL( /*IN*/ const ::rtl::OUString& sURL ) + : ThreadHelpBase( &Application::GetSolarMutex() ) +{ + #ifdef ENABLE_COMPONENT_SELF_CHECK + JobURL::impldbg_checkIt(); + #endif + + m_eRequest = E_UNKNOWN; + + // syntax: vnd.sun.star.job:{[event=<name>],[alias=<name>],[service=<name>]} + + // check for "vnd.sun.star.job:" + if (sURL.matchIgnoreAsciiCaseAsciiL(JOBURL_PROTOCOL_STR,JOBURL_PROTOCOL_LEN,0)) + { + sal_Int32 t = JOBURL_PROTOCOL_LEN; + do + { + // seperate all token of "{[event=<name>],[alias=<name>],[service=<name>]}" + ::rtl::OUString sToken = sURL.getToken(0, JOBURL_PART_SEPERATOR, t); + ::rtl::OUString sPartValue ; + ::rtl::OUString sPartArguments; + + // check for "event=" + if ( + (JobURL::implst_split(sToken,JOBURL_EVENT_STR,JOBURL_EVENT_LEN,sPartValue,sPartArguments)) && + (sPartValue.getLength()>0 ) + ) + { + // set the part value + m_sEvent = sPartValue ; + m_sEventArgs = sPartArguments; + m_eRequest |= E_EVENT ; + } + else + // check for "alias=" + if ( + (JobURL::implst_split(sToken,JOBURL_ALIAS_STR,JOBURL_ALIAS_LEN,sPartValue,sPartArguments)) && + (sPartValue.getLength()>0 ) + ) + { + // set the part value + m_sAlias = sPartValue ; + m_sAliasArgs = sPartArguments; + m_eRequest |= E_ALIAS ; + } + else + // check for "service=" + if ( + (JobURL::implst_split(sToken,JOBURL_SERVICE_STR,JOBURL_SERVICE_LEN,sPartValue,sPartArguments)) && + (sPartValue.getLength()>0 ) + ) + { + // set the part value + m_sService = sPartValue ; + m_sServiceArgs = sPartArguments; + m_eRequest |= E_SERVICE ; + } + } + while(t!=-1); + } +} + +//________________________________ +/** + @short knows, if this job URL object hold a valid URL inside + + @return <TRUE/> if it represent a valid job URL. +*/ +sal_Bool JobURL::isValid() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return (m_eRequest!=E_UNKNOWN); +} + +//________________________________ +/** + @short get the event item of this job URL + @descr Because the three possible parts of such URL (event, alias, service) + can't be combined, this method can(!) return a valid value - but it's + not a must. Thats why the return value must be used too, to detect a missing + event value. + + @param sEvent + returns the possible existing event value + e.g. "vnd.sun.star.job:event=myEvent" returns "myEvent" + + @return <TRUE/> if an event part of the job URL exist and the out parameter + sEvent was filled. + + @attention The out parameter will be reseted everytime. Don't use it if method returns <FALSE/>! +*/ +sal_Bool JobURL::getEvent( /*OUT*/ ::rtl::OUString& sEvent ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + sEvent = ::rtl::OUString(); + sal_Bool bSet = ((m_eRequest & E_EVENT) == E_EVENT); + if (bSet) + sEvent = m_sEvent; + + aReadLock.unlock(); + /* } SAFE */ + + return bSet; +} + +//________________________________ +/** + @short get the alias item of this job URL + @descr Because the three possible parts of such URL (event, alias, service) + can't be combined, this method can(!) return a valid value - but it's + not a must. Thats why the return value must be used too, to detect a missing + alias value. + + @param sAlias + returns the possible existing alias value + e.g. "vnd.sun.star.job:alias=myAlias" returns "myAlias" + + @return <TRUE/> if an alias part of the job URL exist and the out parameter + sAlias was filled. + + @attention The out parameter will be reseted everytime. Don't use it if method returns <FALSE/>! +*/ +sal_Bool JobURL::getAlias( /*OUT*/ ::rtl::OUString& sAlias ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + sAlias = ::rtl::OUString(); + sal_Bool bSet = ((m_eRequest & E_ALIAS) == E_ALIAS); + if (bSet) + sAlias = m_sAlias; + + aReadLock.unlock(); + /* } SAFE */ + + return bSet; +} + +//________________________________ +/** + @short get the service item of this job URL + @descr Because the three possible parts of such URL (event, service, service) + can't be combined, this method can(!) return a valid value - but it's + not a must. Thats why the return value must be used too, to detect a missing + service value. + + @param sAlias + returns the possible existing service value + e.g. "vnd.sun.star.job:service=com.sun.star.Service" returns "com.sun.star.Service" + + @return <TRUE/> if an service part of the job URL exist and the out parameter + sService was filled. + + @attention The out parameter will be reseted everytime. Don't use it if method returns <FALSE/>! +*/ +sal_Bool JobURL::getService( /*OUT*/ ::rtl::OUString& sService ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + sService = ::rtl::OUString(); + sal_Bool bSet = ((m_eRequest & E_SERVICE) == E_SERVICE); + if (bSet) + sService = m_sService; + + aReadLock.unlock(); + /* } SAFE */ + + return bSet; +} + +//________________________________ +/** + @short searches for a special identifier in the given string and split it + @descr If the given identifier could be found at the beginning of the given string, + this method split it into different parts and return it. + Following schema is used: <partidentifier>=<partvalue>[?<partarguments>] + + @param sPart + the string, which should be analyzed + + @param pPartIdentifier + the part identifier value, which must be found at the beginning of the + parameter <var>sPart</var> + + @param nPartLength + the length of the ascii value <var>pPartIdentifier</var> + + @param rPartValue + returns the part value if <var>sPart</var> was splitted successfully + + @param rPartArguments + returns the part arguments if <var>sPart</var> was splitted successfully + + @return <TRUE/> if the identifier could be found and the string was splitted. + <FALSE/> otherwhise. +*/ +sal_Bool JobURL::implst_split( /*IN*/ const ::rtl::OUString& sPart , + /*IN*/ const sal_Char* pPartIdentifier , + /*IN*/ sal_Int32 nPartLength , + /*OUT*/ ::rtl::OUString& rPartValue , + /*OUT*/ ::rtl::OUString& rPartArguments ) +{ + // first search for the given identifier + sal_Bool bPartFound = (sPart.matchIgnoreAsciiCaseAsciiL(pPartIdentifier,nPartLength,0)); + + // If it exist - we can split the part and return TRUE. + // Otherwhise we do nothing and return FALSE. + if (bPartFound) + { + // But may the part has optional arguments - seperated by a "?". + // Do so - we set the return value with the whole part string. + // Arguments will be set to an empty string as default. + // If we detect the right sign - we split the arguments and overwrite the default. + ::rtl::OUString sValueAndArguments = sPart.copy(nPartLength); + ::rtl::OUString sValue = sValueAndArguments ; + ::rtl::OUString sArguments; + + sal_Int32 nArgStart = sValueAndArguments.indexOf('?',0); + if (nArgStart!=-1) + { + sValue = sValueAndArguments.copy(0,nArgStart); + ++nArgStart; // ignore '?'! + sArguments = sValueAndArguments.copy(nArgStart); + } + + rPartValue = sValue ; + rPartArguments = sArguments; + } + + return bPartFound; +} + +//________________________________ +/** + @short special debug method + @descr It's the entry point method to start a self component check for this class. + It's used for internal purposes only and never a part of a legal product. + Use it for testing and debug only! +*/ +#ifdef ENABLE_COMPONENT_SELF_CHECK + +#define LOGFILE_JOBURL "joburl.log" + +void JobURL::impldbg_checkIt() +{ + // check simple URL's + JobURL::impldbg_checkURL("vnd.sun.star.job:event=onMyEvent" , E_EVENT , "onMyEvent", "" , "" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:alias=myAlias" , E_ALIAS , "" , "myAlias", "" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=css.Service", E_SERVICE, "" , "" , "css.Service", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=;" , E_UNKNOWN, "" , "" , "" , NULL, NULL, NULL); + + // check combinations + // Note: No additional spaces or tabs are allowed after a seperator occured. + // Tab and spaces before a seperator will be used as value! + JobURL::impldbg_checkURL("vnd.sun.star.job:event=onMyEvent;alias=myAlias;service=css.Service" , E_EVENT | E_ALIAS | E_SERVICE , "onMyEvent", "myAlias", "css.Service" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=css.Service;alias=myAlias" , E_ALIAS | E_SERVICE , "" , "myAlias", "css.Service" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=css.Service ;alias=myAlias" , E_ALIAS | E_SERVICE , "" , "myAlias", "css.Service ", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=css.Service; alias=myAlias" , E_UNKNOWN , "" , "" , "" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job : event=onMyEvent" , E_UNKNOWN , "" , "" , "" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:event=onMyEvent;event=onMyEvent;service=css.Service", E_UNKNOWN , "" , "" , "" , NULL, NULL, NULL); + + // check upper/lower case + // fix parts of the URL are case insensitive (e.g. "vnd.SUN.star.job:eVEnt=") + // values are case sensitive (e.g. "myAlias" ) + JobURL::impldbg_checkURL("vnd.SUN.star.job:eVEnt=onMyEvent;aliAs=myAlias;serVice=css.Service", E_EVENT | E_ALIAS | E_SERVICE , "onMyEvent", "myAlias", "css.Service" , NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.SUN.star.job:eVEnt=onMyEVENT;aliAs=myALIAS;serVice=css.SERVICE", E_EVENT | E_ALIAS | E_SERVICE , "onMyEVENT", "myALIAS", "css.SERVICE" , NULL, NULL, NULL); + + // check stupid URLs + JobURL::impldbg_checkURL("vnd.sun.star.jobs:service=css.Service" , E_UNKNOWN, "", "", "", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job service=css.Service" , E_UNKNOWN, "", "", "", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service;css.Service" , E_UNKNOWN, "", "", "", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:service;" , E_UNKNOWN, "", "", "", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:;alias;service;event=" , E_UNKNOWN, "", "", "", NULL, NULL, NULL); + JobURL::impldbg_checkURL("vnd.sun.star.job:alias=a;service=s;event=", E_UNKNOWN, "", "", "", NULL, NULL, NULL); + + // check argument handling + JobURL::impldbg_checkURL("vnd.sun.star.job:event=onMyEvent?eventArg1,eventArg2=3,eventArg4," , E_EVENT , "onMyEvent", "" , "" , "eventArg1,eventArg2=3,eventArg4,", NULL , NULL ); + JobURL::impldbg_checkURL("vnd.sun.star.job:alias=myAlias?aliasArg1,aliasarg2" , E_EVENT , "" , "myAlias", "" , NULL , "aliasArg1,aliasarg2", NULL ); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=css.myService?serviceArg1" , E_EVENT , "" , "" , "css.myService", NULL , NULL , "serviceArg1" ); + JobURL::impldbg_checkURL("vnd.sun.star.job:service=css.myService?serviceArg1;alias=myAlias?aliasArg=564", E_EVENT | E_ALIAS, "" , "myAlias", "css.myService", NULL , "aliasArg=564" , "serviceArg1" ); +} + +//________________________________ +/** + @short helper debug method + @descr It uses the given parameter to create a new instance of a JobURL. + They results will be compared with the exepected ones. + The a log will be written, which contains some detailed informations + for this sub test. + + @param pURL + the job URL, which should be checked + + @param eExpectedPart + the expected result + + @param pExpectedEvent + the expected event value + + @param pExpectedAlias + the expected alias value + + @param pExpectedService + the expected service value + + @param pExpectedEventArgs + the expected event arguments + + @param pExpectedAliasArgs + the expected alias arguments + + @param pExpectedServiceArgs + the expected service arguments +*/ +void JobURL::impldbg_checkURL( /*IN*/ const sal_Char* pURL , + /*IN*/ sal_uInt32 eExpectedPart , + /*IN*/ const sal_Char* pExpectedEvent , + /*IN*/ const sal_Char* pExpectedAlias , + /*IN*/ const sal_Char* pExpectedService , + /*IN*/ const sal_Char* pExpectedEventArgs , + /*IN*/ const sal_Char* pExpectedAliasArgs , + /*IN*/ const sal_Char* pExpectedServiceArgs ) +{ + ::rtl::OUString sEvent ; + ::rtl::OUString sAlias ; + ::rtl::OUString sService ; + ::rtl::OUString sEventArgs ; + ::rtl::OUString sAliasArgs ; + ::rtl::OUString sServiceArgs; + ::rtl::OUString sURL (::rtl::OUString::createFromAscii(pURL)); + sal_Bool bOK = sal_True; + + JobURL aURL(sURL); + + // check if URL is invalid + if (eExpectedPart==E_UNKNOWN) + bOK = !aURL.isValid(); + + // check if URL has the expected event part + if ( + (bOK ) && + ((eExpectedPart & E_EVENT) == E_EVENT) + ) + { + bOK = ( + (aURL.isValid() ) && + (aURL.getEvent(sEvent) ) && + (sEvent.getLength()>0 ) && + (sEvent.compareToAscii(pExpectedEvent)==0) + ); + + if (bOK && pExpectedEventArgs!=NULL) + { + bOK = ( + (aURL.getEventArgs(sEventArgs) ) && + (sEventArgs.compareToAscii(pExpectedEventArgs)==0) + ); + }; + } + + // check if URL has no event part + if ( + (bOK ) && + ((eExpectedPart & E_EVENT) != E_EVENT) + ) + { + bOK = ( + (!aURL.getEvent(sEvent) ) && + (sEvent.getLength()==0 ) && + (!aURL.getEventArgs(sEventArgs)) && + (sEventArgs.getLength()==0 ) + ); + } + + // check if URL has the expected alias part + if ( + (bOK ) && + ((eExpectedPart & E_ALIAS) == E_ALIAS) + ) + { + bOK = ( + (aURL.isValid() ) && + (aURL.getAlias(sAlias) ) && + (sAlias.getLength()>0 ) && + (sAlias.compareToAscii(pExpectedAlias)==0) + ); + + if (bOK && pExpectedAliasArgs!=NULL) + { + bOK = ( + (aURL.getAliasArgs(sAliasArgs) ) && + (sAliasArgs.compareToAscii(pExpectedAliasArgs)==0) + ); + }; + } + + // check if URL has the no alias part + if ( + (bOK ) && + ((eExpectedPart & E_ALIAS) != E_ALIAS) + ) + { + bOK = ( + (!aURL.getAlias(sAlias) ) && + (sAlias.getLength()==0 ) && + (!aURL.getAliasArgs(sAliasArgs)) && + (sAliasArgs.getLength()==0 ) + ); + } + + // check if URL has the expected service part + if ( + (bOK ) && + ((eExpectedPart & E_SERVICE) == E_SERVICE) + ) + { + bOK = ( + (aURL.isValid() ) && + (aURL.getService(sService) ) && + (sService.getLength()>0 ) && + (sService.compareToAscii(pExpectedService)==0) + ); + + if (bOK && pExpectedServiceArgs!=NULL) + { + bOK = ( + (aURL.getServiceArgs(sServiceArgs) ) && + (sServiceArgs.compareToAscii(pExpectedServiceArgs)==0) + ); + }; + } + + // check if URL has the no service part + if ( + (bOK ) && + ((eExpectedPart & E_SERVICE) != E_SERVICE) + ) + { + bOK = ( + (!aURL.getService(sService) ) && + (sService.getLength()==0 ) && + (!aURL.getServiceArgs(sServiceArgs)) && + (sServiceArgs.getLength()==0 ) + ); + } + + ::rtl::OUStringBuffer sMsg(256); + + sMsg.appendAscii("\"" ); + sMsg.append (sURL ); + sMsg.appendAscii("\" "); + + if (bOK) + { + sMsg.appendAscii("... OK\n"); + } + else + { + sMsg.appendAscii("... failed\n"); + sMsg.appendAscii("expected was: "); + if (eExpectedPart==E_UNKNOWN) + sMsg.appendAscii("E_UNKNOWN"); + if ((eExpectedPart & E_EVENT) == E_EVENT) + { + sMsg.appendAscii("| E_EVENT e=\""); + sMsg.appendAscii(pExpectedEvent ); + sMsg.appendAscii("\"" ); + } + if ((eExpectedPart & E_ALIAS) == E_ALIAS) + { + sMsg.appendAscii("| E_ALIAS a=\""); + sMsg.appendAscii(pExpectedAlias ); + sMsg.appendAscii("\"" ); + } + if ((eExpectedPart & E_SERVICE) == E_SERVICE) + { + sMsg.appendAscii("| E_SERVICE s=\""); + sMsg.appendAscii(pExpectedService ); + sMsg.appendAscii("\"" ); + } + sMsg.appendAscii("\tbut it was : " ); + sMsg.append (aURL.impldbg_toString()); + sMsg.appendAscii("\n" ); + } + + WRITE_LOGFILE(LOGFILE_JOBURL, U2B(sMsg.makeStringAndClear())) +} + +//________________________________ +/** + @short helper debug method + @descr It returns a representation of the internal object state + as string notation. + + @returns The formated string representation. +*/ +::rtl::OUString JobURL::impldbg_toString() const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + ::rtl::OUStringBuffer sBuffer(256); + + if (m_eRequest==E_UNKNOWN) + sBuffer.appendAscii("E_UNKNOWN"); + if ((m_eRequest & E_EVENT) == E_EVENT) + sBuffer.appendAscii("| E_EVENT"); + if ((m_eRequest & E_ALIAS) == E_ALIAS) + sBuffer.appendAscii("| E_ALIAS"); + if ((m_eRequest & E_SERVICE) == E_SERVICE) + sBuffer.appendAscii("| E_SERVICE"); + sBuffer.appendAscii("{ e=\"" ); + sBuffer.append (m_sEvent ); + sBuffer.appendAscii("\" - a=\""); + sBuffer.append (m_sAlias ); + sBuffer.appendAscii("\" - s=\""); + sBuffer.append (m_sService ); + sBuffer.appendAscii("\" }" ); + + aReadLock.unlock(); + /* } SAFE */ + + return sBuffer.makeStringAndClear(); +} + +//________________________________ + +sal_Bool JobURL::getServiceArgs( /*OUT*/ ::rtl::OUString& sServiceArgs ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + sServiceArgs = ::rtl::OUString(); + sal_Bool bSet = ((m_eRequest & E_SERVICE) == E_SERVICE); + if (bSet) + sServiceArgs = m_sServiceArgs; + + aReadLock.unlock(); + /* } SAFE */ + + return bSet; +} + +//________________________________ + +sal_Bool JobURL::getEventArgs( /*OUT*/ ::rtl::OUString& sEventArgs ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + sEventArgs = ::rtl::OUString(); + sal_Bool bSet = ((m_eRequest & E_EVENT) == E_EVENT); + if (bSet) + sEventArgs = m_sEventArgs; + + aReadLock.unlock(); + /* } SAFE */ + + return bSet; +} + +//________________________________ + +sal_Bool JobURL::getAliasArgs( /*OUT*/ ::rtl::OUString& sAliasArgs ) const +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + sAliasArgs = ::rtl::OUString(); + sal_Bool bSet = ((m_eRequest & E_ALIAS) == E_ALIAS); + if (bSet) + sAliasArgs = m_sAliasArgs; + + aReadLock.unlock(); + /* } SAFE */ + + return bSet; +} + +#endif // ENABLE_COMPONENT_SELF_CHECK + +} // namespace framework diff --git a/framework/source/jobs/makefile.mk b/framework/source/jobs/makefile.mk new file mode 100644 index 000000000000..ee5cc637664a --- /dev/null +++ b/framework/source/jobs/makefile.mk @@ -0,0 +1,53 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ=..$/.. + +PRJNAME= framework +TARGET= fwk_jobs +ENABLE_EXCEPTIONS= TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Generate ----------------------------------------------------- + +SLOFILES= $(SLO)$/jobexecutor.obj \ + $(SLO)$/jobdispatch.obj \ + $(SLO)$/job.obj \ + $(SLO)$/jobdata.obj \ + $(SLO)$/jobresult.obj \ + $(SLO)$/joburl.obj \ + $(SLO)$/jobconst.obj \ + $(SLO)$/helponstartup.obj \ + $(SLO)$/shelljob.obj \ + $(SLO)$/configaccess.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/framework/source/jobs/shelljob.cxx b/framework/source/jobs/shelljob.cxx new file mode 100644 index 000000000000..b7b6d370bb40 --- /dev/null +++ b/framework/source/jobs/shelljob.cxx @@ -0,0 +1,214 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_______________________________________________ +// include own header + +#include <jobs/shelljob.hxx> +#include <jobs/jobconst.hxx> +#include <threadhelp/readguard.hxx> +#include <services.h> + +//_______________________________________________ +// include others + +#include <osl/file.hxx> +#include <osl/process.h> +#include <vcl/svapp.hxx> +#include <rtl/ustrbuf.hxx> +#include <comphelper/sequenceashashmap.hxx> + +//_______________________________________________ +// include interfaces + +#include <com/sun/star/system/XSystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/util/XStringSubstitution.hpp> + +//_______________________________________________ +// namespace + +namespace framework{ + +//_______________________________________________ +// definitions + +/** adress job configuration inside argument set provided on method execute(). */ +static const ::rtl::OUString PROP_JOBCONFIG = ::rtl::OUString::createFromAscii("JobConfig"); + +/** adress job configuration property "Command". */ +static const ::rtl::OUString PROP_COMMAND = ::rtl::OUString::createFromAscii("Command"); + +/** adress job configuration property "Arguments". */ +static const ::rtl::OUString PROP_ARGUMENTS = ::rtl::OUString::createFromAscii("Arguments"); + +/** adress job configuration property "DeactivateJobIfDone". */ +static const ::rtl::OUString PROP_DEACTIVATEJOBIFDONE = ::rtl::OUString::createFromAscii("DeactivateJobIfDone"); + +/** adress job configuration property "CheckExitCode". */ +static const ::rtl::OUString PROP_CHECKEXITCODE = ::rtl::OUString::createFromAscii("CheckExitCode"); + +//----------------------------------------------- + +DEFINE_XSERVICEINFO_MULTISERVICE(ShellJob , + ::cppu::OWeakObject , + SERVICENAME_JOB , + IMPLEMENTATIONNAME_SHELLJOB) + +DEFINE_INIT_SERVICE(ShellJob, + { + /* Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + } + ) + +//----------------------------------------------- +ShellJob::ShellJob(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) + : ThreadHelpBase( ) + , m_xSMGR (xSMGR) +{ +} + +//----------------------------------------------- +ShellJob::~ShellJob() +{ +} + +//----------------------------------------------- +css::uno::Any SAL_CALL ShellJob::execute(const css::uno::Sequence< css::beans::NamedValue >& lJobArguments) + throw(css::lang::IllegalArgumentException, + css::uno::Exception , + css::uno::RuntimeException ) +{ + ::comphelper::SequenceAsHashMap lArgs (lJobArguments); + ::comphelper::SequenceAsHashMap lOwnCfg(lArgs.getUnpackedValueOrDefault(PROP_JOBCONFIG, css::uno::Sequence< css::beans::NamedValue >())); + + const ::rtl::OUString sCommand = lOwnCfg.getUnpackedValueOrDefault(PROP_COMMAND , ::rtl::OUString()); + const css::uno::Sequence< ::rtl::OUString > lCommandArguments = lOwnCfg.getUnpackedValueOrDefault(PROP_ARGUMENTS , css::uno::Sequence< ::rtl::OUString >()); + const ::sal_Bool bDeactivateJobIfDone = lOwnCfg.getUnpackedValueOrDefault(PROP_DEACTIVATEJOBIFDONE , sal_True ); + const ::sal_Bool bCheckExitCode = lOwnCfg.getUnpackedValueOrDefault(PROP_CHECKEXITCODE , sal_True ); + + // replace all might existing place holder. + ::rtl::OUString sRealCommand = impl_substituteCommandVariables(sCommand); + + // Command is required as minimum. + // If it does not exists ... we cant do our job. + // Deactivate such miss configured job silently .-) + if (sRealCommand.getLength() < 1) + return ShellJob::impl_generateAnswer4Deactivation(); + + // do it + ::sal_Bool bDone = impl_execute(sRealCommand, lCommandArguments, bCheckExitCode); + if (! bDone) + return css::uno::Any(); + + // Job was done ... user configured deactivation of this job + // in such case. + if (bDeactivateJobIfDone) + return ShellJob::impl_generateAnswer4Deactivation(); + + // There was no decision about deactivation of this job. + // So we have to return nothing here ! + return css::uno::Any(); +} + +//----------------------------------------------- +css::uno::Any ShellJob::impl_generateAnswer4Deactivation() +{ + css::uno::Sequence< css::beans::NamedValue > aAnswer(1); + aAnswer[0].Name = JobConst::ANSWER_DEACTIVATE_JOB(); + aAnswer[0].Value = css::uno::makeAny(sal_True); + + return css::uno::makeAny(aAnswer); +} + +//----------------------------------------------- +::rtl::OUString ShellJob::impl_substituteCommandVariables(const ::rtl::OUString& sCommand) +{ + // SYNCHRONIZED -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SYNCHRONIZED + + try + { + css::uno::Reference< css::util::XStringSubstitution > xSubst ( xSMGR->createInstance(SERVICENAME_SUBSTITUTEPATHVARIABLES), css::uno::UNO_QUERY_THROW); + const ::sal_Bool bSubstRequired = sal_True; + const ::rtl::OUString sCompleteCommand = xSubst->substituteVariables(sCommand, bSubstRequired); + + return sCompleteCommand; + } + catch(const css::uno::Exception&) + {} + + return ::rtl::OUString(); +} + +//----------------------------------------------- +::sal_Bool ShellJob::impl_execute(const ::rtl::OUString& sCommand , + const css::uno::Sequence< ::rtl::OUString >& lArguments , + ::sal_Bool bCheckExitCode) +{ + ::rtl_uString** pArgs = NULL; + const ::sal_Int32 nArgs = lArguments.getLength (); + oslProcessOption nOptions = osl_Process_WAIT; + oslProcess hProcess(0); + + if (nArgs > 0) + pArgs = reinterpret_cast< ::rtl_uString** >(const_cast< ::rtl::OUString* >(lArguments.getConstArray())); + + oslProcessError eError = osl_executeProcess(sCommand.pData, pArgs, nArgs, nOptions, NULL, NULL, NULL, 0, &hProcess); + + // executable not found or couldnt be started + if (eError != osl_Process_E_None) + return sal_False; + + ::sal_Bool bRet = sal_True; + if (bCheckExitCode) + { + // check its return codes ... + oslProcessInfo aInfo; + aInfo.Size = sizeof (oslProcessInfo); + eError = osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo); + + if (eError != osl_Process_E_None) + bRet = sal_False; + else + bRet = (aInfo.Code == 0); + } + osl_freeProcessHandle(hProcess); + return bRet; +} + +} // namespace framework |