merge the formfield patch from ooo-build
[ooovba.git] / framework / source / jobs / jobdata.cxx
blobbb04f4f09ffe85c337bc790b6c85abdfb65c6e95
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: jobdata.cxx,v $
10 * $Revision: 1.10.82.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_framework.hxx"
34 //________________________________
35 // my own includes
36 #include <jobs/jobdata.hxx>
37 #include <threadhelp/readguard.hxx>
38 #include <threadhelp/writeguard.hxx>
39 #include <classes/converter.hxx>
40 #include <general.h>
41 #include <services.h>
43 //________________________________
44 // interface includes
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp>
47 #include <com/sun/star/container/XNameAccess.hpp>
48 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
50 //________________________________
51 // includes of other projects
52 #include <tools/wldcrd.hxx>
53 #include <unotools/configpathes.hxx>
54 #include <rtl/ustrbuf.hxx>
55 #include <vcl/svapp.hxx>
57 //________________________________
58 // namespace
60 namespace framework{
62 //________________________________
63 // exported const
65 const sal_Char* JobData::JOBCFG_ROOT = "/org.openoffice.Office.Jobs/Jobs/" ;
66 const sal_Char* JobData::JOBCFG_PROP_SERVICE = "Service" ;
67 const sal_Char* JobData::JOBCFG_PROP_ARGUMENTS = "Arguments" ;
69 const sal_Char* JobData::EVENTCFG_ROOT = "/org.openoffice.Office.Jobs/Events/" ;
70 const sal_Char* JobData::EVENTCFG_PATH_JOBLIST = "/JobList" ;
71 const sal_Char* JobData::EVENTCFG_PROP_ADMINTIME = "AdminTime" ;
72 const sal_Char* JobData::EVENTCFG_PROP_USERTIME = "UserTime" ;
74 const sal_Char* JobData::PROPSET_CONFIG = "Config" ;
75 const sal_Char* JobData::PROPSET_OWNCONFIG = "JobConfig" ;
76 const sal_Char* JobData::PROPSET_ENVIRONMENT = "Environment" ;
77 const sal_Char* JobData::PROPSET_DYNAMICDATA = "DynamicData" ;
79 const sal_Char* JobData::PROP_ALIAS = "Alias" ;
80 const sal_Char* JobData::PROP_EVENTNAME = "EventName" ;
81 const sal_Char* JobData::PROP_ENVTYPE = "EnvType" ;
82 const sal_Char* JobData::PROP_FRAME = "Frame" ;
83 const sal_Char* JobData::PROP_MODEL = "Model" ;
84 const sal_Char* JobData::PROP_SERVICE = "Service" ;
86 //________________________________
87 // non exported definitions
89 //________________________________
90 // declarations
92 //________________________________
93 /**
94 @short standard ctor
95 @descr It initialize this new instance.
96 But for real working it's neccessary to call setAlias() or setService() later.
97 Because we need the job data ...
99 @param xSMGR
100 reference to the uno service manager
102 JobData::JobData( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR )
103 : ThreadHelpBase(&Application::GetSolarMutex())
104 , m_xSMGR (xSMGR )
106 // share code for member initialization with defaults!
107 impl_reset();
110 //________________________________
112 @short copy ctor
113 @descr Sometimes such job data container must be moved from one using place
114 to another one. Then a copy ctor and copy operator must be available.
116 @param rCopy
117 the original instance, from which we must copy all data
119 JobData::JobData( const JobData& rCopy )
120 : ThreadHelpBase(&Application::GetSolarMutex())
122 // use the copy operator to share the same code
123 *this = rCopy;
126 //________________________________
128 @short operator for coping JobData instances
129 @descr Sometimes such job data container must be moved from one using place
130 to another one. Then a copy ctor and copy operator must be available.
132 @param rCopy
133 the original instance, from which we must copy all data
135 void JobData::operator=( const JobData& rCopy )
137 /* SAFE { */
138 WriteGuard aWriteLock(m_aLock);
139 // Please don't copy the uno service manager reference.
140 // That can change the uno context, which isn't a good idea!
141 m_eMode = rCopy.m_eMode ;
142 m_eEnvironment = rCopy.m_eEnvironment ;
143 m_sAlias = rCopy.m_sAlias ;
144 m_sService = rCopy.m_sService ;
145 m_sEvent = rCopy.m_sEvent ;
146 m_lArguments = rCopy.m_lArguments ;
147 m_aLastExecutionResult = rCopy.m_aLastExecutionResult;
148 aWriteLock.unlock();
149 /* } SAFE */
152 //________________________________
154 @short let this instance die
155 @descr There is no chance any longer to work. We have to
156 release all used ressources and free used memory.
158 JobData::~JobData()
160 impl_reset();
163 //________________________________
165 @short initalize this instance as a job with configuration
166 @descr They given alias can be used to adress some configuraton data.
167 We read it and fill our internal structures. Of course old informations
168 will be lost doing so.
170 @param sAlias
171 the alias name of this job, used to locate job properties inside cfg
173 void JobData::setAlias( const ::rtl::OUString& sAlias )
175 /* SAFE { */
176 WriteGuard aWriteLock(m_aLock);
177 // delete all old informations! Otherwhise we mix it with the new one ...
178 impl_reset();
180 // take over the new informations
181 m_sAlias = sAlias;
182 m_eMode = E_ALIAS;
184 // try to open the configuration set of this job directly and get a property access to it
185 // We open it readonly here
186 ::rtl::OUString sKey;
187 sKey = ::rtl::OUString::createFromAscii(JOBCFG_ROOT);
188 sKey += ::utl::wrapConfigurationElementName(m_sAlias);
190 ConfigAccess aConfig(m_xSMGR, sKey);
191 aConfig.open(ConfigAccess::E_READONLY);
192 if (aConfig.getMode()==ConfigAccess::E_CLOSED)
194 impl_reset();
195 return;
198 css::uno::Reference< css::beans::XPropertySet > xJobProperties(aConfig.cfg(), css::uno::UNO_QUERY);
199 if (xJobProperties.is())
201 css::uno::Any aValue;
203 // read uno implementation name
204 aValue = xJobProperties->getPropertyValue(::rtl::OUString::createFromAscii(JOBCFG_PROP_SERVICE));
205 aValue >>= m_sService;
207 // read whole argument list
208 aValue = xJobProperties->getPropertyValue(::rtl::OUString::createFromAscii(JOBCFG_PROP_ARGUMENTS));
209 css::uno::Reference< css::container::XNameAccess > xArgumentList;
210 if (
211 (aValue >>= xArgumentList) &&
212 (xArgumentList.is() )
215 css::uno::Sequence< ::rtl::OUString > lArgumentNames = xArgumentList->getElementNames();
216 sal_Int32 nCount = lArgumentNames.getLength();
217 m_lArguments.realloc(nCount);
218 for (sal_Int32 i=0; i<nCount; ++i)
220 m_lArguments[i].Name = lArgumentNames[i];
221 m_lArguments[i].Value = xArgumentList->getByName(m_lArguments[i].Name);
226 aConfig.close();
227 aWriteLock.unlock();
228 /* } SAFE */
231 //________________________________
233 @short initalize this instance as a job without configuration
234 @descr This job has no configuration data. We have to forget all old informations
235 and set only some of them new, so this instance can work.
237 @param sService
238 the uno service name of this "non configured" job
240 void JobData::setService( const ::rtl::OUString& sService )
242 /* SAFE { */
243 WriteGuard aWriteLock(m_aLock);
245 // delete all old informations! Otherwhise we mix it with the new one ...
246 impl_reset();
247 // take over the new informations
248 m_sService = sService;
249 m_eMode = E_SERVICE;
251 aWriteLock.unlock();
252 /* } SAFE */
255 //________________________________
257 @short initialize this instance with new job values.
258 @descr It reads automaticly all properties of the specified
259 job (using it's alias name) and "register it" for the
260 given event. This registration will not be validated against
261 the underlying configuration! (That must be done from outside.
262 Because the caller must have the configuration already open to
263 get the values for sEvent and sAlias! And doing so it can perform
264 only, if the time stanp values are readed outside too.
265 Further it make no sense to initialize and start a disabled job.
266 So this initialization method will be called for enabled jobs only.)
268 @param sEvent
269 the triggered event, for which this job should be started
271 @param sAlias
272 mark the required job inside event registration list
274 void JobData::setEvent( const ::rtl::OUString& sEvent ,
275 const ::rtl::OUString& sAlias )
277 // share code to read all job properties!
278 setAlias(sAlias);
280 /* SAFE { */
281 WriteGuard aWriteLock(m_aLock);
283 // take over the new informations - which differ against set on of method setAlias()!
284 m_sEvent = sEvent;
285 m_eMode = E_EVENT;
287 aWriteLock.unlock();
288 /* } SAFE */
291 //________________________________
293 @short set the new job specific arguments
294 @descr If a job finish his work, it can give us a new list of arguments (which
295 will not interpreted by us). We write it back to the configuration only
296 (if this job has it's own configuration!).
297 So a job can have persistent data without implementing anything
298 or define own config areas for that.
300 @param lArguments
301 list of arguments, which should be set for this job
303 void JobData::setJobConfig( const css::uno::Sequence< css::beans::NamedValue >& lArguments )
305 /* SAFE { */
306 WriteGuard aWriteLock(m_aLock);
308 // actualize member
309 m_lArguments = lArguments;
311 // actualize the configuration ... if possible!
312 if (m_eMode==E_ALIAS)
314 // It doesn't matter if this config object was already opened before.
315 // It doesn nothing here then ... or it change the mode automaticly, if
316 // it was opened using another one before.
317 ::rtl::OUString sKey;
318 sKey = ::rtl::OUString::createFromAscii(JOBCFG_ROOT);
319 sKey += ::utl::wrapConfigurationElementName(m_sAlias);
321 ConfigAccess aConfig(m_xSMGR, sKey);
322 aConfig.open(ConfigAccess::E_READWRITE);
323 if (aConfig.getMode()==ConfigAccess::E_CLOSED)
324 return;
326 css::uno::Reference< css::beans::XMultiHierarchicalPropertySet > xArgumentList(aConfig.cfg(), css::uno::UNO_QUERY);
327 if (xArgumentList.is())
329 sal_Int32 nCount = m_lArguments.getLength();
330 css::uno::Sequence< ::rtl::OUString > lNames (nCount);
331 css::uno::Sequence< css::uno::Any > lValues(nCount);
333 for (sal_Int32 i=0; i<nCount; ++i)
335 lNames [i] = m_lArguments[i].Name ;
336 lValues[i] = m_lArguments[i].Value;
339 xArgumentList->setHierarchicalPropertyValues(lNames, lValues);
341 aConfig.close();
344 aWriteLock.unlock();
345 /* } SAFE */
348 //________________________________
350 @short set a new excution result
351 @descr Every executed job can have returned a result.
352 We set it here, so our user can use it may be later.
353 But the outside code can use it too, to analyze it and
354 adopt the configuration of this job too. Because the
355 result uses a protocol, which allow that. And we provide
356 right functionality to save it.
358 @param aResult
359 the result of last execution
361 void JobData::setResult( const JobResult& aResult )
363 /* SAFE { */
364 WriteGuard aWriteLock(m_aLock);
366 // overwrite the last saved result
367 m_aLastExecutionResult = aResult;
369 // Don't use his informations to actualize
370 // e.g. the arguments of this job. It must be done
371 // from outside! Here we save this information only.
373 aWriteLock.unlock();
374 /* } SAFE */
377 //________________________________
379 @short set a new environment descriptor for this job
380 @descr It must(!) be done everytime this container is initialized
381 with new job datas e.g.: setAlias()/setEvent()/setService() ...
382 Otherwhise the environment will be unknown!
384 void JobData::setEnvironment( EEnvironment eEnvironment )
386 /* SAFE { */
387 WriteGuard aWriteLock(m_aLock);
388 m_eEnvironment = eEnvironment;
389 aWriteLock.unlock();
390 /* } SAFE */
393 //________________________________
395 @short these functions provides access to our internal members
396 @descr These member represent any information about the job
397 and can be used from outside to e.g. start a job.
399 JobData::EMode JobData::getMode() const
401 /* SAFE { */
402 ReadGuard aReadLock(m_aLock);
403 return m_eMode;
404 /* } SAFE */
407 //________________________________
409 JobData::EEnvironment JobData::getEnvironment() const
411 /* SAFE { */
412 ReadGuard aReadLock(m_aLock);
413 return m_eEnvironment;
414 /* } SAFE */
417 //________________________________
419 ::rtl::OUString JobData::getEnvironmentDescriptor() const
421 ::rtl::OUString sDescriptor;
422 /* SAFE { */
423 ReadGuard aReadLock(m_aLock);
424 switch(m_eEnvironment)
426 case E_EXECUTION :
427 sDescriptor = ::rtl::OUString::createFromAscii("EXECUTOR");
428 break;
430 case E_DISPATCH :
431 sDescriptor = ::rtl::OUString::createFromAscii("DISPATCH");
432 break;
434 case E_DOCUMENTEVENT :
435 sDescriptor = ::rtl::OUString::createFromAscii("DOCUMENTEVENT");
436 break;
437 default:
438 break;
440 /* } SAFE */
441 return sDescriptor;
444 //________________________________
446 ::rtl::OUString JobData::getService() const
448 /* SAFE { */
449 ReadGuard aReadLock(m_aLock);
450 return m_sService;
451 /* } SAFE */
454 //________________________________
456 ::rtl::OUString JobData::getEvent() const
458 /* SAFE { */
459 ReadGuard aReadLock(m_aLock);
460 return m_sEvent;
461 /* } SAFE */
464 //________________________________
466 css::uno::Sequence< css::beans::NamedValue > JobData::getJobConfig() const
468 /* SAFE { */
469 ReadGuard aReadLock(m_aLock);
470 return m_lArguments;
471 /* } SAFE */
474 //________________________________
476 css::uno::Sequence< css::beans::NamedValue > JobData::getConfig() const
478 /* SAFE { */
479 ReadGuard aReadLock(m_aLock);
480 css::uno::Sequence< css::beans::NamedValue > lConfig;
481 if (m_eMode==E_ALIAS)
483 lConfig.realloc(2);
484 sal_Int32 i = 0;
486 lConfig[i].Name = ::rtl::OUString::createFromAscii(PROP_ALIAS);
487 lConfig[i].Value <<= m_sAlias;
488 ++i;
490 lConfig[i].Name = ::rtl::OUString::createFromAscii(PROP_SERVICE);
491 lConfig[i].Value <<= m_sService;
492 ++i;
494 aReadLock.unlock();
495 /* } SAFE */
496 return lConfig;
499 //________________________________
501 @short return information, if this job is part of the global configuration package
502 org.openoffice.Office.Jobs
503 @descr Because jobs can be executed by the dispatch framework using an uno service name
504 directly - an executed job must not have any configuration realy. Such jobs
505 must provide the right interfaces only! But after finishing jobs can return
506 some informations (e.g. for updating her configuration ...). We must know
507 if such request is valid or not then.
509 @return TRUE if the represented job is part of the underlying configuration package.
511 sal_Bool JobData::hasConfig() const
513 /* SAFE { */
514 ReadGuard aReadLock(m_aLock);
515 return (m_eMode==E_ALIAS || m_eMode==E_EVENT);
516 /* } SAFE */
519 //________________________________
521 @short mark a job as non startable for further requests
522 @descr We don't remove the configuration entry! We set a timestamp value only.
523 And there exist two of them: one for an administrator ... and one for the
524 current user. We change it for the user layer only. So this JobDispatch can't be
525 started any more ... till the administrator change his timestamp.
526 That can be usefull for post setup scenarios, which must run one time only.
528 Note: This method don't do anything, if ths represented job doesn't have a configuration!
530 void JobData::disableJob()
532 /* SAFE { */
533 WriteGuard aWriteLock(m_aLock);
535 // No configuration - not used from EXECUTOR and not triggered from an event => no chance!
536 if (m_eMode!=E_EVENT)
537 return;
539 // actualize the configuration
540 // It doesn't matter if this config object was already opened before.
541 // It doesn nothing here then ... or it change the mode automaticly, if
542 // it was opened using another one before.
543 ::rtl::OUStringBuffer sKey(256);
544 sKey.appendAscii(JobData::EVENTCFG_ROOT );
545 sKey.append (::utl::wrapConfigurationElementName(m_sEvent));
546 sKey.appendAscii(JobData::EVENTCFG_PATH_JOBLIST );
547 sKey.appendAscii("/" );
548 sKey.append (::utl::wrapConfigurationElementName(m_sAlias));
550 ConfigAccess aConfig(m_xSMGR, sKey.makeStringAndClear());
551 aConfig.open(ConfigAccess::E_READWRITE);
552 if (aConfig.getMode()==ConfigAccess::E_CLOSED)
553 return;
555 css::uno::Reference< css::beans::XPropertySet > xPropSet(aConfig.cfg(), css::uno::UNO_QUERY);
556 if (xPropSet.is())
558 // Convert and write the user timestamp to the configuration.
559 css::uno::Any aValue;
560 aValue <<= Converter::convert_DateTime2ISO8601(DateTime());
561 xPropSet->setPropertyValue(::rtl::OUString::createFromAscii(EVENTCFG_PROP_USERTIME), aValue);
564 aConfig.close();
566 aWriteLock.unlock();
567 /* } SAFE */
570 //________________________________
573 sal_Bool isEnabled( const ::rtl::OUString& sAdminTime ,
574 const ::rtl::OUString& sUserTime )
576 /*Attention!
577 To prevent interpreting of TriGraphs inside next const string value,
578 we have to encode all '?' signs. Otherwhise e.g. "??-" will be translated
579 to "~" ...
581 static ::rtl::OUString PATTERN_ISO8601 = ::rtl::OUString::createFromAscii("\?\?\?\?-\?\?-\?\?*\0");
582 WildCard aISOPattern(PATTERN_ISO8601);
584 sal_Bool bValidAdmin = aISOPattern.Matches(sAdminTime);
585 sal_Bool bValidUser = aISOPattern.Matches(sUserTime );
587 // We check for "isEnabled()" here only.
588 // Note further: ISO8601 formated strings can be compared as strings directly!
589 return (
590 (!bValidAdmin && !bValidUser ) ||
591 ( bValidAdmin && bValidUser && sAdminTime>=sUserTime)
595 //________________________________
598 void JobData::appendEnabledJobsForEvent( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ,
599 const ::rtl::OUString& sEvent ,
600 ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding >& lJobs )
602 css::uno::Sequence< ::rtl::OUString > lAdditionalJobs = JobData::getEnabledJobsForEvent(xSMGR, sEvent);
603 sal_Int32 c = lAdditionalJobs.getLength();
604 sal_Int32 i = 0;
606 for (i=0; i<c; ++i)
608 JobData::TJob2DocEventBinding aBinding(lAdditionalJobs[i], sEvent);
609 lJobs.push_back(aBinding);
613 //________________________________
616 css::uno::Sequence< ::rtl::OUString > JobData::getEnabledJobsForEvent( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ,
617 const ::rtl::OUString& sEvent )
619 // these static values may perform following loop for reading time stamp values ...
620 static ::rtl::OUString ADMINTIME = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_PROP_ADMINTIME);
621 static ::rtl::OUString USERTIME = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_PROP_USERTIME );
622 static ::rtl::OUString ROOT = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_ROOT );
623 static ::rtl::OUString JOBLIST = ::rtl::OUString::createFromAscii(JobData::EVENTCFG_PATH_JOBLIST );
625 // create a config access to "/org.openoffice.Office.Jobs/Events"
626 ConfigAccess aConfig(xSMGR,ROOT);
627 aConfig.open(ConfigAccess::E_READONLY);
628 if (aConfig.getMode()==ConfigAccess::E_CLOSED)
629 return css::uno::Sequence< ::rtl::OUString >();
631 css::uno::Reference< css::container::XHierarchicalNameAccess > xEventRegistry(aConfig.cfg(), css::uno::UNO_QUERY);
632 if (!xEventRegistry.is())
633 return css::uno::Sequence< ::rtl::OUString >();
635 // check if the given event exist inside list of registered ones
636 ::rtl::OUString sPath(sEvent);
637 sPath += JOBLIST;
638 if (!xEventRegistry->hasByHierarchicalName(sPath))
639 return css::uno::Sequence< ::rtl::OUString >();
641 // step to the job list, which is a child of the event node inside cfg
642 // e.g. "/org.openoffice.Office.Jobs/Events/<event name>/JobList"
643 css::uno::Any aJobList = xEventRegistry->getByHierarchicalName(sPath);
644 css::uno::Reference< css::container::XNameAccess > xJobList;
645 if (!(aJobList >>= xJobList) || !xJobList.is())
646 return css::uno::Sequence< ::rtl::OUString >();
648 // get all alias names of jobs, which are part of this job list
649 // But Some of them can be disabled by it's time stamp values.
650 // We create an additional job name list iwth the same size, then the original list ...
651 // step over all job entries ... check her time stamps ... and put only job names to the
652 // destination list, which represent an enabled job.
653 css::uno::Sequence< ::rtl::OUString > lAllJobs = xJobList->getElementNames();
654 ::rtl::OUString* pAllJobs = lAllJobs.getArray();
655 sal_Int32 c = lAllJobs.getLength();
657 css::uno::Sequence< ::rtl::OUString > lEnabledJobs(c);
658 ::rtl::OUString* pEnabledJobs = lEnabledJobs.getArray();
659 sal_Int32 d = 0;
661 for (sal_Int32 s=0; s<c; ++s)
663 css::uno::Reference< css::beans::XPropertySet > xJob;
664 if (
665 !(xJobList->getByName(pAllJobs[s]) >>= xJob) ||
666 !(xJob.is() )
669 continue;
672 ::rtl::OUString sAdminTime;
673 xJob->getPropertyValue(ADMINTIME) >>= sAdminTime;
675 ::rtl::OUString sUserTime;
676 xJob->getPropertyValue(USERTIME) >>= sUserTime;
678 if (!isEnabled(sAdminTime, sUserTime))
679 continue;
681 pEnabledJobs[d] = pAllJobs[s];
682 ++d;
684 lEnabledJobs.realloc(d);
686 aConfig.close();
688 return lEnabledJobs;
691 //________________________________
693 @short reset all internal structures
694 @descr If somehwere recycle this instance, he can switch from one
695 using mode to another one. But then we have to reset all currently
696 used informations. Otherwhise we mix it and they can make trouble.
698 But note: that does not set defaults for internal used members, which
699 does not relate to any job property! e.g. the reference to the global
700 uno service manager. Such informations are used for internal processes only
701 and are neccessary for our work.
703 void JobData::impl_reset()
705 /* SAFE { */
706 WriteGuard aWriteLock(m_aLock);
707 m_eMode = E_UNKNOWN_MODE;
708 m_eEnvironment = E_UNKNOWN_ENVIRONMENT;
709 m_sAlias = ::rtl::OUString();
710 m_sService = ::rtl::OUString();
711 m_sEvent = ::rtl::OUString();
712 m_lArguments = css::uno::Sequence< css::beans::NamedValue >();
713 aWriteLock.unlock();
714 /* } SAFE */
717 } // namespace framework