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 <dispatch/servicehandler.hxx> |
21 | #include <threadhelp/readguard.hxx> |
22 | #include <general.h> |
23 | #include <services.h> |
24 | |
25 | #include <com/sun/star/frame/DispatchResultState.hpp> |
26 | #include <com/sun/star/task/XJobExecutor.hpp> |
27 | |
28 | #include <vcl/svapp.hxx> |
29 | |
30 | namespace framework{ |
31 | |
32 | #define PROTOCOL_VALUE "service:" |
33 | #define PROTOCOL_LENGTH 8 |
34 | |
35 | //_________________________________________________________________________________________________________________ |
36 | // XInterface, XTypeProvider, XServiceInfo |
37 | |
38 | DEFINE_XSERVICEINFO_MULTISERVICE(ServiceHandler , |
39 | ::cppu::OWeakObject , |
40 | SERVICENAME_PROTOCOLHANDLER , |
41 | IMPLEMENTATIONNAME_SERVICEHANDLER) |
42 | |
43 | DEFINE_INIT_SERVICE(ServiceHandler, |
44 | { |
45 | /*Attention |
46 | I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() |
47 | to create a new instance of this class by our own supported service factory. |
48 | see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further information! |
49 | */ |
50 | } |
51 | ) |
52 | |
53 | //_________________________________________________________________________________________________________________ |
54 | |
55 | /** |
56 | @short standard ctor |
57 | @descr These initialize a new instance of ths class with needed information for work. |
58 | |
59 | @param xFactory |
60 | reference to uno servicemanager for creation of new services |
61 | */ |
62 | ServiceHandler::ServiceHandler( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) |
63 | // Init baseclasses first |
64 | : ThreadHelpBase( &Application::GetSolarMutex() ) |
65 | // Init member |
66 | , m_xFactory ( xFactory ) |
67 | { |
68 | } |
69 | |
70 | //_________________________________________________________________________________________________________________ |
71 | |
72 | /** |
73 | @short standard dtor |
74 | @descr - |
75 | */ |
76 | ServiceHandler::~ServiceHandler() |
77 | { |
78 | m_xFactory = NULL; |
79 | } |
80 | |
81 | //_________________________________________________________________________________________________________________ |
82 | |
83 | /** |
84 | @short decide if this dispatch implementation can be used for requested URL or not |
85 | @descr A protocol handler is registerd for an URL pattern inside configuration and will |
86 | be asked by the generic dispatch mechanism inside framework, if he can handle this |
87 | special URL which match his registration. He can agree by returning of a valid dispatch |
88 | instance or disagree by returning <NULL/>. |
89 | We don't create new dispatch instances here really - we return THIS as result to handle it |
90 | at the same implementation. |
91 | */ |
92 | css::uno::Reference< css::frame::XDispatch > SAL_CALL ServiceHandler::queryDispatch( const css::util::URL& aURL , |
93 | const OUString& /*sTarget*/ , |
94 | sal_Int32 /*nFlags*/ ) throw( css::uno::RuntimeException ) |
95 | { |
96 | css::uno::Reference< css::frame::XDispatch > xDispatcher; |
97 | if (aURL.Complete.startsWith(PROTOCOL_VALUE)) |
98 | xDispatcher = this; |
99 | return xDispatcher; |
100 | } |
101 | |
102 | //_________________________________________________________________________________________________________________ |
103 | |
104 | /** |
105 | @short do the same like dispatch() but for multiple requests at the same time |
106 | @descr - |
107 | */ |
108 | css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL ServiceHandler::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) throw( css::uno::RuntimeException ) |
109 | { |
110 | sal_Int32 nCount = lDescriptor.getLength(); |
111 | css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); |
112 | for( sal_Int32 i=0; i<nCount; ++i ) |
113 | { |
114 | lDispatcher[i] = this->queryDispatch( |
115 | lDescriptor[i].FeatureURL, |
116 | lDescriptor[i].FrameName, |
117 | lDescriptor[i].SearchFlags); |
118 | } |
119 | return lDispatcher; |
120 | } |
121 | |
122 | //_________________________________________________________________________________________________________________ |
123 | |
124 | /** |
125 | @short dispatch URL with arguments |
126 | @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it. |
127 | Because we don't support status listener notifications here. |
128 | |
129 | @param aURL |
130 | uno URL which should be executed |
131 | @param lArguments |
132 | list of optional arguments for this request |
133 | */ |
134 | void SAL_CALL ServiceHandler::dispatch( const css::util::URL& aURL , |
135 | const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException ) |
136 | { |
137 | // dispatch() is an [oneway] call ... and may our user release his reference to us immediately. |
138 | // So we should hold us self alive till this call ends. |
139 | css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); |
140 | implts_dispatch(aURL,lArguments); |
141 | // No notification for status listener! |
142 | } |
143 | |
144 | //_________________________________________________________________________________________________________________ |
145 | |
146 | /** |
147 | @short dispatch with guaranteed notifications about success |
148 | @descr We use threadsafe internal method to do so. Return state of this function will be used |
149 | for notification if an optional listener is given. |
150 | |
151 | @param aURL |
152 | uno URL which should be executed |
153 | @param lArguments |
154 | list of optional arguments for this request |
155 | @param xListener |
156 | optional listener for state events |
157 | */ |
158 | void SAL_CALL ServiceHandler::dispatchWithNotification( const css::util::URL& aURL , |
159 | const css::uno::Sequence< css::beans::PropertyValue >& lArguments, |
160 | const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) throw( css::uno::RuntimeException ) |
161 | { |
162 | // This class was designed to die by reference. And if user release his reference to us immediately after calling this method |
163 | // we can run into some problems. So we hold us self alive till this method ends. |
164 | // Another reason: We can use this reference as source of sending event at the end too. |
165 | css::uno::Reference< css::frame::XNotifyingDispatch > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); |
166 | |
167 | css::uno::Reference< css::uno::XInterface > xService = implts_dispatch(aURL,lArguments); |
168 | if (xListener.is()) |
169 | { |
170 | css::frame::DispatchResultEvent aEvent; |
171 | if (xService.is()) |
172 | aEvent.State = css::frame::DispatchResultState::SUCCESS; |
173 | else |
174 | aEvent.State = css::frame::DispatchResultState::FAILURE; |
175 | aEvent.Result <<= xService; // may NULL for state=FAILED! |
176 | aEvent.Source = xThis; |
177 | |
178 | xListener->dispatchFinished( aEvent ); |
179 | } |
180 | } |
181 | |
182 | //_________________________________________________________________________________________________________________ |
183 | |
184 | /** |
185 | @short threadsafe helper for dispatch calls |
186 | @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal |
187 | function. It implements the real dispatch operation and returns a state value which inform caller |
188 | about success. He can notify listener then by using this return value. |
189 | |
190 | @param aURL |
191 | uno URL which should be executed |
192 | @param lArguments |
193 | list of optional arguments for this request |
194 | |
195 | @return <NULL/> if requested service couldn't be created successullfy; |
196 | a valid reference otherwise. This return value can be used to indicate, |
197 | if dispatch was successfully or not. |
198 | */ |
199 | css::uno::Reference< css::uno::XInterface > ServiceHandler::implts_dispatch( const css::util::URL& aURL , |
200 | const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ ) throw( css::uno::RuntimeException ) |
201 | { |
202 | /* SAFE */ |
203 | ReadGuard aReadLock( m_aLock ); |
204 | css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory; |
205 | aReadLock.unlock(); |
206 | /* SAFE */ |
207 | |
208 | if (!xFactory.is()) |
209 | return css::uno::Reference< css::uno::XInterface >(); |
210 | |
211 | // extract service name and may optional given parameters from given URL |
212 | // and use it to create and start the component |
213 | OUString sServiceAndArguments = aURL.Complete.copy(PROTOCOL_LENGTH); |
214 | OUString sServiceName; |
215 | OUString sArguments ; |
216 | |
217 | sal_Int32 nArgStart = sServiceAndArguments.indexOf('?',0); |
218 | if (nArgStart!=-1) |
219 | { |
220 | sServiceName = sServiceAndArguments.copy(0,nArgStart); |
221 | ++nArgStart; // ignore '?'! |
222 | sArguments = sServiceAndArguments.copy(nArgStart); |
223 | } |
224 | else |
225 | { |
226 | sServiceName = sServiceAndArguments; |
227 | } |
228 | |
229 | if (sServiceName.isEmpty()) |
230 | return css::uno::Reference< css::uno::XInterface >(); |
231 | |
232 | // If a service doesn't support an optional job executor interface - he can't get |
233 | // any given parameters! |
234 | // Because we can't know if we must call createInstanceWithArguments() or XJobExecutor::trigger() ... |
235 | |
236 | css::uno::Reference< css::uno::XInterface > xService; |
237 | try |
238 | { |
239 | // => a) a service starts running inside his own ctor and we create it only |
240 | xService = xFactory->createInstance(sServiceName); |
241 | // or b) he implements the right interface and starts there (may with optional parameters) |
242 | css::uno::Reference< css::task::XJobExecutor > xExecuteable(xService, css::uno::UNO_QUERY); |
243 | if (xExecuteable.is()) |
244 | xExecuteable->trigger(sArguments); |
245 | } |
246 | // ignore all errors - inclusive runtime errors! |
247 | // E.g. a script based service (written in phyton) could not be executed |
248 | // because it contains syntax errors, which was detected at runtime ... |
249 | catch(const css::uno::Exception& e) |
250 | { |
251 | SAL_WARN( |
252 | "fwk.dispatch" , "ignored UNO Exception \"" << e.Message << '"'); |
253 | xService.clear(); |
254 | } |
255 | |
256 | return xService; |
257 | } |
258 | |
259 | //_________________________________________________________________________________________________________________ |
260 | |
261 | /** |
262 | @short add/remove listener for state events |
263 | @descr We use an internal container to hold such registered listener. This container lives if we live. |
264 | And if call pas registration as non breakable transaction - we can accept the request without |
265 | any explicit lock. Because we share our mutex with this container. |
266 | |
267 | @param xListener |
268 | reference to a valid listener for state events |
269 | @param aURL |
270 | URL about listener will be informed, if something occurred |
271 | */ |
272 | void SAL_CALL ServiceHandler::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , |
273 | const css::util::URL& /*aURL*/ ) throw( css::uno::RuntimeException ) |
274 | { |
275 | // not suported yet |
276 | } |
277 | |
278 | //_________________________________________________________________________________________________________________ |
279 | |
280 | void SAL_CALL ServiceHandler::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , |
281 | const css::util::URL& /*aURL*/ ) throw( css::uno::RuntimeException ) |
282 | { |
283 | // not suported yet |
284 | } |
285 | |
286 | } // namespace framework |
287 | |
288 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
289 | |