LibreOffice Module framework (master) 1
sessionlistener.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/types.h>
21#include <sal/log.hxx>
22
23#include <framework/desktop.hxx>
24
26#include <com/sun/star/beans/NamedValue.hpp>
27#include <com/sun/star/beans/PropertyValue.hpp>
28#include <com/sun/star/frame/theAutoRecovery.hpp>
29#include <com/sun/star/frame/Desktop.hpp>
30#include <com/sun/star/frame/FeatureStateEvent.hpp>
31#include <com/sun/star/frame/XDispatch.hpp>
32#include <com/sun/star/frame/XSessionManagerListener2.hpp>
33#include <com/sun/star/frame/XSessionManagerClient.hpp>
34#include <com/sun/star/frame/XStatusListener.hpp>
35#include <com/sun/star/lang/EventObject.hpp>
36#include <com/sun/star/lang/XInitialization.hpp>
37#include <com/sun/star/util/URLTransformer.hpp>
38#include <com/sun/star/util/XURLTransformer.hpp>
39#include <com/sun/star/util/URL.hpp>
42
43#include <com/sun/star/uno/Any.hxx>
44#include <com/sun/star/uno/Sequence.hxx>
45#include <utility>
46
47using namespace css;
48using namespace com::sun::star::uno;
49using namespace com::sun::star::util;
50using namespace com::sun::star::beans;
51using namespace framework;
52
53namespace {
54
56
73typedef cppu::WeakImplHelper<
74 css::lang::XInitialization,
75 css::frame::XSessionManagerListener2,
76 css::frame::XStatusListener,
77 css::lang::XServiceInfo> SessionListener_BASE;
78
79class SessionListener : public SessionListener_BASE
80{
81private:
82 osl::Mutex m_aMutex;
83
86 css::uno::Reference< css::uno::XComponentContext > m_xContext;
87
88 css::uno::Reference< css::frame::XSessionManagerClient > m_rSessionManager;
89
90 // restore handling
91 bool m_bRestored;
92
93 bool m_bSessionStoreRequested;
94
95 bool m_bAllowUserInteractionOnQuit;
96 bool m_bTerminated;
97
98 // in case of synchronous call the caller should do saveDone() call himself!
99 void StoreSession( bool bAsync );
100
101 // let session quietly close the documents, remove lock files, store configuration and etc.
102 void QuitSessionQuietly();
103
104public:
105 explicit SessionListener(css::uno::Reference< css::uno::XComponentContext > xContext);
106
107 virtual ~SessionListener() override;
108
109 virtual OUString SAL_CALL getImplementationName() override
110 {
111 return "com.sun.star.comp.frame.SessionListener";
112 }
113
114 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
115 {
116 return cppu::supportsService(this, ServiceName);
117 }
118
119 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
120 {
121 return {"com.sun.star.frame.SessionListener"};
122 }
123
124 virtual void SAL_CALL disposing(const css::lang::EventObject&) override;
125
126 // XInitialization
127 virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& args) override;
128
129 // XSessionManagerListener
130 virtual void SAL_CALL doSave( sal_Bool bShutdown, sal_Bool bCancelable ) override;
131 virtual void SAL_CALL approveInteraction( sal_Bool bInteractionGranted ) override;
132 virtual void SAL_CALL shutdownCanceled() override;
133 virtual sal_Bool SAL_CALL doRestore() override;
134
135 // XSessionManagerListener2
136 virtual void SAL_CALL doQuit() override;
137
138 // XStatusListener
139 virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& event) override;
140};
141
142SessionListener::SessionListener(css::uno::Reference< css::uno::XComponentContext > rxContext )
143 : m_xContext(std::move( rxContext ))
144 , m_bRestored( false )
145 , m_bSessionStoreRequested( false )
146 , m_bAllowUserInteractionOnQuit( false )
147 , m_bTerminated( false )
148{
149 SAL_INFO("fwk.session", "SessionListener::SessionListener");
150}
151
152SessionListener::~SessionListener()
153{
154 SAL_INFO("fwk.session", "SessionListener::~SessionListener");
155 if (m_rSessionManager.is())
156 {
157 css::uno::Reference< XSessionManagerListener> me(this);
158 m_rSessionManager->removeSessionManagerListener(me);
159 }
160}
161
162void SessionListener::StoreSession( bool bAsync )
163{
164 SAL_INFO("fwk.session", "SessionListener::StoreSession");
165 osl::MutexGuard g(m_aMutex);
166 try
167 {
168 // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
169 // xd->dispatch("vnd.sun.star.autorecovery:/doSessionSave, async=bAsync
170 // on stop event m_rSessionManager->saveDone(this); in case of asynchronous call
171 // in case of synchronous call the caller should do saveDone() call himself!
172
173 css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
174 css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
175 URL aURL;
176 aURL.Complete = "vnd.sun.star.autorecovery:/doSessionSave";
177 xURLTransformer->parseStrict(aURL);
178
179 // in case of asynchronous call the notification will trigger saveDone()
180 if ( bAsync )
181 xDispatch->addStatusListener(this, aURL);
182
183 Sequence< PropertyValue > args{ PropertyValue("DispatchAsynchron",-1,Any(bAsync),
184 PropertyState_DIRECT_VALUE) };
185 xDispatch->dispatch(aURL, args);
186 } catch (const css::uno::Exception&) {
187 TOOLS_WARN_EXCEPTION("fwk.session", "");
188 // save failed, but tell manager to go on if we haven't yet dispatched the request
189 // in case of synchronous saving the notification is done by the caller
190 if ( bAsync && m_rSessionManager.is() )
191 m_rSessionManager->saveDone(this);
192 }
193}
194
195void SessionListener::QuitSessionQuietly()
196{
197 SAL_INFO("fwk.session", "SessionListener::QuitSessionQuietly");
198 osl::MutexGuard g(m_aMutex);
199 try
200 {
201 // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
202 // xd->dispatch("vnd.sun.star.autorecovery:/doSessionQuietQuit, async=false
203 // it is done synchronously to avoid conflict with normal quit process
204
205 css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
206 css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
207 URL aURL;
208 aURL.Complete = "vnd.sun.star.autorecovery:/doSessionQuietQuit";
209 xURLTransformer->parseStrict(aURL);
210
211 Sequence< PropertyValue > args{ PropertyValue("DispatchAsynchron",-1,Any(false),
212 PropertyState_DIRECT_VALUE) };
213 xDispatch->dispatch(aURL, args);
214 } catch (const css::uno::Exception&) {
215 TOOLS_WARN_EXCEPTION("fwk.session", "");
216 }
217}
218
219void SAL_CALL SessionListener::disposing(const css::lang::EventObject& Source)
220{
221 SAL_INFO("fwk.session", "SessionListener::disposing");
222 if (Source.Source == m_rSessionManager) {
223 m_rSessionManager.clear();
224 }
225}
226
227void SAL_CALL SessionListener::initialize(const Sequence< Any >& args)
228{
229 SAL_INFO("fwk.session", "SessionListener::initialize");
230
231 OUString aSMgr("com.sun.star.frame.SessionManagerClient");
232 if ( (args.getLength() == 1) && (args[0] >>= m_bAllowUserInteractionOnQuit) )
233 ;// do nothing
234 else if (args.hasElements())
235 {
236 NamedValue v;
237 for (const Any& rArg : args)
238 {
239 if (rArg >>= v)
240 {
241 if ( v.Name == "SessionManagerName" )
242 v.Value >>= aSMgr;
243 else if ( v.Name == "SessionManager" )
244 v.Value >>= m_rSessionManager;
245 else if ( v.Name == "AllowUserInteractionOnQuit" )
246 v.Value >>= m_bAllowUserInteractionOnQuit;
247 }
248 }
249 }
250
251 SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false"));
252 if (!m_rSessionManager.is())
253 m_rSessionManager = css::uno::Reference< frame::XSessionManagerClient >
254 (m_xContext->getServiceManager()->createInstanceWithContext(aSMgr, m_xContext), UNO_QUERY);
255
256 if (m_rSessionManager.is())
257 {
258 m_rSessionManager->addSessionManagerListener(this);
259 }
260}
261
262void SAL_CALL SessionListener::statusChanged(const frame::FeatureStateEvent& event)
263{
264 SAL_INFO("fwk.session", "SessionListener::statusChanged");
265
266 SAL_INFO("fwk.session.debug", " ev.Feature = " << event.FeatureURL.Complete <<
267 ", ev.Descript = " << event.FeatureDescriptor);
268 if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doSessionRestore" )
269 {
270 if (event.FeatureDescriptor == "update")
271 m_bRestored = true; // a document was restored
272
273 }
274 else if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doAutoSave" )
275 { // the "doSessionSave" was never set, look to framework/source/services/autorecovery.cxx
276 // it always testing but never setting (enum AutoRecovery::E_SESSION_SAVE)
277 if (event.FeatureDescriptor == "update")
278 {
279 if (m_rSessionManager.is())
280 m_rSessionManager->saveDone(this); // done with save
281 }
282 }
283}
284
285sal_Bool SAL_CALL SessionListener::doRestore()
286{
287 SAL_INFO("fwk.session", "SessionListener::doRestore");
288 osl::MutexGuard g(m_aMutex);
289 m_bRestored = false;
290 try {
291 css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
292
293 URL aURL;
294 aURL.Complete = "vnd.sun.star.autorecovery:/doSessionRestore";
295 css::uno::Reference< XURLTransformer > xURLTransformer(URLTransformer::create(m_xContext));
296 xURLTransformer->parseStrict(aURL);
298 xDispatch->addStatusListener(this, aURL);
299 xDispatch->dispatch(aURL, args);
300 m_bRestored = true;
301
302 } catch (const css::uno::Exception&) {
303 TOOLS_WARN_EXCEPTION("fwk.session", "");
304 }
305
306 return m_bRestored;
307}
308
309void SAL_CALL SessionListener::doSave( sal_Bool bShutdown, sal_Bool /*bCancelable*/ )
310{
311 SAL_INFO("fwk.session", "SessionListener::doSave");
312
313 SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false") <<
314 ", bShutdown = " << (bShutdown ? "true" : "false"));
315 if (bShutdown)
316 {
317 m_bSessionStoreRequested = true; // there is no need to protect it with mutex
318 if ( m_bAllowUserInteractionOnQuit && m_rSessionManager.is() )
319 m_rSessionManager->queryInteraction( static_cast< css::frame::XSessionManagerListener* >( this ) );
320 else
321 StoreSession( true );
322 }
323 // we don't have anything to do so tell the session manager we're done
324 else if( m_rSessionManager.is() )
325 m_rSessionManager->saveDone( this );
326}
327
328void SAL_CALL SessionListener::approveInteraction( sal_Bool bInteractionGranted )
329{
330 SAL_INFO("fwk.session", "SessionListener::approveInteraction");
331 // do AutoSave as the first step
332 osl::MutexGuard g(m_aMutex);
333
334 if ( bInteractionGranted )
335 {
336 // close the office documents in normal way
337 try
338 {
339 // first of all let the session be stored to be sure that we lose no information
340 StoreSession( false );
341
342 css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
343 // honestly: how many implementations of XDesktop will we ever have?
344 // so casting this directly to the implementation
345 Desktop* pDesktop(dynamic_cast<Desktop*>(xDesktop.get()));
346 if(pDesktop)
347 {
348 SAL_INFO("fwk.session", " XDesktop is a framework::Desktop -- good.");
349 m_bTerminated = pDesktop->terminateQuickstarterToo();
350 }
351 else
352 {
353 SAL_WARN("fwk.session", " XDesktop is not a framework::Desktop -- this should never happen.");
354 m_bTerminated = xDesktop->terminate();
355 }
356
357 if ( m_rSessionManager.is() )
358 {
359 // false means that the application closing has been cancelled
360 if ( !m_bTerminated )
361 m_rSessionManager->cancelShutdown();
362 else
363 m_rSessionManager->interactionDone( this );
364 }
365 }
366 catch( const css::uno::Exception& )
367 {
368 StoreSession( true );
369 m_rSessionManager->interactionDone( this );
370 }
371
372 if ( m_rSessionManager.is() && m_bTerminated )
373 m_rSessionManager->saveDone(this);
374 }
375 else
376 {
377 StoreSession( true );
378 }
379}
380
381void SessionListener::shutdownCanceled()
382{
383 SAL_INFO("fwk.session", "SessionListener::shutdownCanceled");
384 // set the state back
385 m_bSessionStoreRequested = false; // there is no need to protect it with mutex
386
387 if ( m_rSessionManager.is() )
388 m_rSessionManager->saveDone(this);
389}
390
391void SessionListener::doQuit()
392{
393 SAL_INFO("fwk.session", "SessionListener::doQuit");
394 if ( m_bSessionStoreRequested && !m_bTerminated )
395 {
396 // let the session be closed quietly in this case
397 QuitSessionQuietly();
398 }
399}
400
401}
402
403extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
405 css::uno::XComponentContext *context,
406 css::uno::Sequence<css::uno::Any> const &)
407{
408 SAL_INFO("fwk.session", "com_sun_star_comp_frame_SessionListener_get_implementation");
409
410 return cppu::acquire(new SessionListener(context));
411}
412
413/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool m_bTerminated
#define TOOLS_WARN_EXCEPTION(area, stream)
Reference< XDispatch > xDispatch
URL aURL
float v
css::uno::Reference< css::uno::XComponentContext > m_xContext
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
args
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_frame_SessionListener_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
unsigned char sal_Bool
std::mutex m_aMutex