LibreOffice Module extensions (master) 1
updatecheckjob.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include "updatecheck.hxx"
23#include "updatecheckconfig.hxx"
24#include "updatehdl.hxx"
25#include "updateprotocol.hxx"
26
27#include <memory>
28#include <mutex>
29#include <utility>
30
34
35#include <com/sun/star/frame/Desktop.hpp>
36#include <com/sun/star/frame/XTerminateListener.hpp>
37#include <com/sun/star/task/XJob.hpp>
38
39namespace beans = com::sun::star::beans ;
40namespace frame = com::sun::star::frame ;
41namespace lang = com::sun::star::lang ;
42namespace task = com::sun::star::task ;
43namespace uno = com::sun::star::uno ;
44
45namespace
46{
47
48class InitUpdateCheckJobThread : public osl::Thread
49{
50public:
51 InitUpdateCheckJobThread( const uno::Reference< uno::XComponentContext > &xContext,
52 const uno::Sequence< beans::NamedValue > &xParameters,
53 bool bShowDialog );
54
55 virtual void SAL_CALL run() override;
56
57 void setTerminating();
58
59private:
60 osl::Condition m_aCondition;
61 uno::Reference<uno::XComponentContext> m_xContext;
62 uno::Sequence<beans::NamedValue> m_xParameters;
63 bool m_bShowDialog;
64 bool m_bTerminating;
65
66 std::mutex m_mutex;
67 rtl::Reference<UpdateCheck> m_controller;
68};
69
70class UpdateCheckJob :
71 public ::cppu::WeakImplHelper< task::XJob, lang::XServiceInfo, frame::XTerminateListener >
72{
73 virtual ~UpdateCheckJob() override;
74
75public:
76
77 UpdateCheckJob(
78 css::uno::Reference<css::uno::XComponentContext> const & context,
79 css::uno::Reference<css::frame::XDesktop2> const & desktop):
81 {}
82
83 // XJob
84 virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
85
86 // XServiceInfo
87 virtual OUString SAL_CALL getImplementationName() override;
88 virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
89 virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
90
91 // XEventListener
92 virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
93
94 // XTerminateListener
95 virtual void SAL_CALL queryTermination( lang::EventObject const & evt ) override;
96 virtual void SAL_CALL notifyTermination( lang::EventObject const & evt ) override;
97
98private:
99 uno::Reference<uno::XComponentContext> m_xContext;
100
101 std::mutex m_mutex;
102 uno::Reference< frame::XDesktop2 > m_xDesktop;
103 std::unique_ptr< InitUpdateCheckJobThread > m_pInitThread;
104
105 void handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp );
106 void terminateAndJoinThread();
107};
108
109InitUpdateCheckJobThread::InitUpdateCheckJobThread(
110 const uno::Reference< uno::XComponentContext > &xContext,
111 const uno::Sequence< beans::NamedValue > &xParameters,
112 bool bShowDialog ) :
113 m_xContext( xContext ),
114 m_xParameters( xParameters ),
115 m_bShowDialog( bShowDialog ),
116 m_bTerminating( false )
117{
118 create();
119}
120
121
122void SAL_CALL InitUpdateCheckJobThread::run()
123{
124 osl_setThreadName("InitUpdateCheckJobThread");
125
126 if (!m_bShowDialog) {
127 TimeValue tv = { 25, 0 };
128 m_aCondition.wait( &tv );
129 if ( m_bTerminating )
130 return;
131 }
132
133 try {
134 rtl::Reference< UpdateCheck > aController( UpdateCheck::get() );
135 // At least for the automatic ("onFirstVisibleTask", i.e., !m_bShowDialog) check, wait for
136 // m_controller during setTerminating, to prevent m_controller from still having threads
137 // running during exit (ideally, we would make sure that all threads are joined before exit,
138 // but the UpdateCheck logic is rather convoluted, so play it safe for now and only address
139 // the automatic update check that is known to cause issues during `make check`, not the
140 // manually triggered update check scenario):
141 if (!m_bShowDialog) {
142 std::scoped_lock l(m_mutex);
143 m_controller = aController;
144 }
145 aController->initialize( m_xParameters, m_xContext );
146
147 if ( m_bShowDialog )
148 aController->showDialog( true );
149 } catch (const uno::Exception &) {
150 // fdo#64962 - don't bring the app down on some unexpected exception.
151 TOOLS_WARN_EXCEPTION("extensions.update", "Caught init update exception, thread terminated" );
152 {
153 std::scoped_lock l(m_mutex);
154 m_controller.clear();
155 }
156 }
157}
158
159void InitUpdateCheckJobThread::setTerminating() {
160 m_bTerminating = true;
161 m_aCondition.set();
163 {
164 std::scoped_lock l(m_mutex);
165 std::swap(controller, m_controller);
166 }
167 if (controller.is()) {
168 controller->waitForUpdateCheckFinished();
169 }
170}
171
172UpdateCheckJob::~UpdateCheckJob()
173{
174}
175
177UpdateCheckJob::execute(const uno::Sequence<beans::NamedValue>& namedValues)
178{
179 for ( sal_Int32 n=namedValues.getLength(); n-- > 0; )
180 {
181 if ( namedValues[ n ].Name == "DynamicData" )
182 {
183 uno::Sequence<beans::NamedValue> aListProp;
184 if ( namedValues[n].Value >>= aListProp )
185 {
186 for ( sal_Int32 i=aListProp.getLength(); i-- > 0; )
187 {
188 if ( aListProp[ i ].Name == "updateList" )
189 {
190 handleExtensionUpdates( aListProp );
191 return uno::Any();
192 }
193 }
194 }
195 }
196 }
197
198 uno::Sequence<beans::NamedValue> aConfig =
199 getValue< uno::Sequence<beans::NamedValue> > (namedValues, "JobConfig");
200
201 /* Determine the way we got invoked here -
202 * see Developers Guide Chapter "4.7.2 Jobs" to understand the magic
203 */
204
205 uno::Sequence<beans::NamedValue> aEnvironment =
206 getValue< uno::Sequence<beans::NamedValue> > (namedValues, "Environment");
207
208 OUString aEventName = getValue< OUString > (aEnvironment, "EventName");
209
210 auto thread = std::make_unique<InitUpdateCheckJobThread >(
211 m_xContext, aConfig,
212 aEventName != "onFirstVisibleTask");
213 {
214 std::scoped_lock l(m_mutex);
215 m_pInitThread = std::move(thread);
216 }
217
218 return uno::Any();
219}
220
221
222void UpdateCheckJob::handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp )
223{
224 try {
225 uno::Sequence< uno::Sequence< OUString > > aList =
226 getValue< uno::Sequence< uno::Sequence< OUString > > > ( rListProp, "updateList" );
227 bool bPrepareOnly = getValue< bool > ( rListProp, "prepareOnly" );
228
229 // we will first store any new found updates and then check, if there are any
230 // pending updates.
232
233 if ( bPrepareOnly )
234 return;
235
236 bool bHasUpdates = checkForPendingUpdates( m_xContext );
237
238 rtl::Reference<UpdateCheck> aController( UpdateCheck::get() );
239 if ( ! aController.is() )
240 return;
241
242 aController->setHasExtensionUpdates( bHasUpdates );
243
244 if ( ! aController->hasOfficeUpdate() )
245 {
246 if ( bHasUpdates )
247 aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL, true );
248 else
249 aController->setUIState( UPDATESTATE_NO_UPDATE_AVAIL, true );
250 }
251 }
252 catch( const uno::Exception& )
253 {
254 TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated");
255 }
256}
257
258
259OUString SAL_CALL
260UpdateCheckJob::getImplementationName()
261{
262 return "vnd.sun.UpdateCheck";
263}
264
265
266uno::Sequence< OUString > SAL_CALL
267UpdateCheckJob::getSupportedServiceNames()
268{
269 return { "com.sun.star.setup.UpdateCheck" };
270}
271
272sal_Bool SAL_CALL
273UpdateCheckJob::supportsService( OUString const & serviceName )
274{
275 return cppu::supportsService(this, serviceName);
276}
277
278
279// XEventListener
280void SAL_CALL UpdateCheckJob::disposing( lang::EventObject const & rEvt )
281{
282 css::uno::Reference<css::frame::XDesktop2> desktop;
283 {
284 std::scoped_lock l(m_mutex);
285 if ( rEvt.Source == m_xDesktop ) {
286 std::swap(m_xDesktop, desktop);
287 }
288 }
289
290 if ( desktop.is() )
291 {
292 terminateAndJoinThread();
293 desktop->removeTerminateListener( this );
294 }
295}
296
297
298// XTerminateListener
299void SAL_CALL UpdateCheckJob::queryTermination( lang::EventObject const & )
300{
301}
302
303void UpdateCheckJob::terminateAndJoinThread()
304{
305 std::unique_ptr<InitUpdateCheckJobThread> thread;
306 {
307 std::scoped_lock l(m_mutex);
308 std::swap(m_pInitThread, thread);
309 }
310 if (thread != nullptr)
311 {
312 thread->setTerminating();
313 thread->join();
314 }
315}
316
317void SAL_CALL UpdateCheckJob::notifyTermination( lang::EventObject const & )
318{
319 terminateAndJoinThread();
320}
321
322} // anonymous namespace
323
324extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
326 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
327{
328 css::uno::Reference<css::frame::XDesktop2> desktop(
329 css::frame::Desktop::create(context));
330 rtl::Reference<UpdateCheckJob> job(new UpdateCheckJob(context, desktop));
331 desktop->addTerminateListener(job);
332 return cppu::acquire(job.get());
333}
334
335
336/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XDesktop2 > m_xDesktop
#define TOOLS_WARN_EXCEPTION(area, stream)
Reference< XComponentContext > m_xContext
Definition: filehandler.cxx:78
Mutex m_mutex
def run(arg=None, arg2=-1)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
unsigned char sal_Bool
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * extensions_update_UpdateCheckJob_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
@ UPDATESTATE_EXT_UPD_AVAIL
Definition: updatehdl.hxx:68
@ UPDATESTATE_NO_UPDATE_AVAIL
Definition: updatehdl.hxx:60
bool storeExtensionUpdateInfos(const uno::Reference< uno::XComponentContext > &rxContext, const uno::Sequence< uno::Sequence< OUString > > &rUpdateInfos)
bool checkForPendingUpdates(const uno::Reference< uno::XComponentContext > &rxContext)