1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
22 #include <string_view>
25 #include <sal/log.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <osl/diagnose.h>
28 #include <osl/mutex.hxx>
29 #include <osl/conditn.hxx>
31 #include <rtl/process.h>
32 #include <rtl/ref.hxx>
34 #include <cppuhelper/bootstrap.hxx>
35 #include <cppuhelper/implbase.hxx>
37 #include <com/sun/star/lang/XMain.hpp>
38 #include <com/sun/star/lang/XInitialization.hpp>
39 #include <com/sun/star/lang/XComponent.hpp>
40 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
41 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
42 #include <com/sun/star/lang/XEventListener.hpp>
43 #include <com/sun/star/loader/XImplementationLoader.hpp>
44 #include <com/sun/star/registry/XRegistryKey.hpp>
45 #include <com/sun/star/connection/Acceptor.hpp>
46 #include <com/sun/star/connection/XConnection.hpp>
47 #include <com/sun/star/bridge/XBridgeFactory.hpp>
48 #include <com/sun/star/bridge/XBridge.hpp>
53 using namespace com::sun::star::uno
;
54 using namespace com::sun::star::lang
;
55 using namespace com::sun::star::loader
;
56 using namespace com::sun::star::registry
;
57 using namespace com::sun::star::connection
;
58 using namespace com::sun::star::bridge
;
59 using namespace com::sun::star::container
;
64 static bool s_quiet
= false;
66 static void out( const char * pText
)
69 fputs( pText
, stderr
);
72 static void out( std::u16string_view rText
)
76 OString
aText( OUStringToOString( rText
, RTL_TEXTENCODING_ASCII_US
) );
77 fputs( aText
.getStr(), stderr
);
81 const char arUsingText
[] =
83 "uno [-c ComponentImplementationName -l LocationUrl | -s ServiceName]\n"
84 " [-u uno:(socket[,host=HostName][,port=nnn]|pipe[,name=PipeName]);<protocol>;Name\n"
85 " [--singleaccept] [--singleinstance]]\n"
87 " [-- Argument1 Argument2 ...]\n";
89 /// @throws RuntimeException
90 static bool readOption( OUString
* pValue
, const char * pOpt
,
91 sal_uInt32
* pnIndex
, const OUString
& aArg
)
93 static const OUStringLiteral
dash(u
"-");
94 if(!aArg
.startsWith(dash
))
97 OUString aOpt
= OUString::createFromAscii( pOpt
);
99 if (aArg
.getLength() < aOpt
.getLength())
102 if (aOpt
.equalsIgnoreAsciiCase( aArg
.subView(1, aArg
.getLength()-1) ))
104 // take next argument
107 rtl_getAppCommandArg(*pnIndex
, &pValue
->pData
);
108 if (*pnIndex
>= rtl_getAppCommandArgCount() || pValue
->subView(1) == dash
)
110 throw RuntimeException( "incomplete option \"-" + aOpt
+ "\" given!" );
112 SAL_INFO("cpputools.unoexe", "> identified option -" << pOpt
<< " = " << aArg
);
116 else if (aArg
.indexOf(aOpt
) == 1)
118 *pValue
= aArg
.copy(1 + aOpt
.getLength());
119 SAL_INFO("cpputools.unoexe", "> identified option -" << pOpt
<< " = " << aArg
);
127 static bool readOption( bool * pbOpt
, const char * pOpt
,
128 sal_uInt32
* pnIndex
, std::u16string_view aArg
)
130 OUString aOpt
= OUString::createFromAscii(pOpt
);
132 if(o3tl::starts_with(aArg
, u
"--") && aOpt
== aArg
.substr(2))
136 SAL_INFO("cpputools.unoexe", "> identified option --" << pOpt
);
142 /// @throws Exception
144 static void createInstance(
145 Reference
< T
> & rxOut
,
146 const Reference
< XComponentContext
> & xContext
,
147 const OUString
& rServiceName
)
149 Reference
< XMultiComponentFactory
> xMgr( xContext
->getServiceManager() );
150 Reference
< XInterface
> x( xMgr
->createInstanceWithContext( rServiceName
, xContext
) );
154 throw RuntimeException( "cannot get service instance \"" + rServiceName
+ "\"!" );
157 rxOut
.set( x
.get(), UNO_QUERY_THROW
);
160 /// @throws Exception
161 static Reference
< XInterface
> loadComponent(
162 const Reference
< XComponentContext
> & xContext
,
163 const OUString
& rImplName
, const OUString
& rLocation
)
165 // determine loader to be used
166 sal_Int32 nDot
= rLocation
.lastIndexOf( '.' );
167 if (nDot
<= 0 || nDot
>= rLocation
.getLength())
169 throw RuntimeException(
170 "location \"" + rLocation
+ "\" has no extension! Cannot determine loader to be used!" );
173 Reference
< XImplementationLoader
> xLoader
;
175 std::u16string_view
aExt( rLocation
.subView( nDot
+1 ) );
177 if (aExt
== u
"dll" || aExt
== u
"exe" || aExt
== u
"dylib" || aExt
== u
"so")
180 xLoader
, xContext
, "com.sun.star.loader.SharedLibrary" );
182 else if (aExt
== u
"jar" || aExt
== u
"class")
185 xLoader
, xContext
, "com.sun.star.loader.Java" );
189 throw RuntimeException(
190 "unknown extension of \"" + rLocation
+ "\"! No loader available!" );
193 Reference
< XInterface
> xInstance
;
196 Reference
< XInterface
> xFactory( xLoader
->activate(
197 rImplName
, OUString(), rLocation
, Reference
< XRegistryKey
>() ) );
200 Reference
< XSingleComponentFactory
> xCFac( xFactory
, UNO_QUERY
);
203 xInstance
= xCFac
->createInstanceWithContext( xContext
);
207 Reference
< XSingleServiceFactory
> xSFac( xFactory
, UNO_QUERY
);
210 out( "\n> warning: ignoring context for implementation \"" );
213 xInstance
= xSFac
->createInstance();
218 if (! xInstance
.is())
220 throw RuntimeException(
221 "activating component \"" + rImplName
+ "\" from location \"" + rLocation
+ "\" failed!" );
229 class OInstanceProvider
230 : public WeakImplHelper
< XInstanceProvider
>
232 Reference
< XComponentContext
> _xContext
;
234 std::mutex _aSingleInstanceMutex
;
235 Reference
< XInterface
> _xSingleInstance
;
236 bool _bSingleInstance
;
240 OUString _aServiceName
;
241 Sequence
< Any
> _aInitParams
;
243 OUString _aInstanceName
;
245 /// @throws Exception
246 inline Reference
< XInterface
> createInstance() const;
249 OInstanceProvider( const Reference
< XComponentContext
> & xContext
,
250 OUString aImplName
, OUString aLocation
,
251 OUString aServiceName
, const Sequence
< Any
> & rInitParams
,
252 bool bSingleInstance
, OUString aInstanceName
)
253 : _xContext( xContext
)
254 , _bSingleInstance( bSingleInstance
)
255 , _aImplName(std::move( aImplName
))
256 , _aLocation(std::move( aLocation
))
257 , _aServiceName(std::move( aServiceName
))
258 , _aInitParams( rInitParams
)
259 , _aInstanceName(std::move( aInstanceName
))
263 virtual Reference
< XInterface
> SAL_CALL
getInstance( const OUString
& rName
) override
;
268 inline Reference
< XInterface
> OInstanceProvider::createInstance() const
270 Reference
< XInterface
> xRet
;
271 if (!_aImplName
.isEmpty()) // manually via loader
272 xRet
= loadComponent( _xContext
, _aImplName
, _aLocation
);
273 else // via service manager
274 unoexe::createInstance( xRet
, _xContext
, _aServiceName
);
277 Reference
< XInitialization
> xInit( xRet
, UNO_QUERY
);
279 xInit
->initialize( _aInitParams
);
284 Reference
< XInterface
> OInstanceProvider::getInstance( const OUString
& rName
)
288 if (_aInstanceName
== rName
)
290 Reference
< XInterface
> xRet
;
292 if (_aImplName
.isEmpty() && _aServiceName
.isEmpty())
294 OSL_ASSERT( rName
== "uno.ComponentContext" );
297 else if (_bSingleInstance
)
299 if (! _xSingleInstance
.is())
301 std::lock_guard
aGuard( _aSingleInstanceMutex
);
302 if (! _xSingleInstance
.is())
304 _xSingleInstance
= createInstance();
307 xRet
= _xSingleInstance
;
311 xRet
= createInstance();
317 catch (Exception
& rExc
)
319 out( "\n> error: " );
322 throw NoSuchElementException(
323 "no such element \"" + rName
+ "\"!" );
328 struct ODisposingListener
: public WeakImplHelper
< XEventListener
>
333 virtual void SAL_CALL
disposing( const EventObject
& rEvt
) override
;
335 static void waitFor( const Reference
< XComponent
> & xComp
);
340 void ODisposingListener::disposing( const EventObject
& )
345 void ODisposingListener::waitFor( const Reference
< XComponent
> & xComp
)
347 rtl::Reference
<ODisposingListener
> xListener
= new ODisposingListener
;
349 xComp
->addEventListener( xListener
);
350 xListener
->cDisposed
.wait();
353 } // namespace unoexe
355 using namespace unoexe
;
359 sal_uInt32 nCount
= rtl_getAppCommandArgCount();
367 Reference
< XComponentContext
> xContext
;
372 OUString aImplName
, aLocation
, aServiceName
, aUnoUrl
;
373 Sequence
< OUString
> aParams
;
374 bool bSingleAccept
= false;
375 bool bSingleInstance
= false;
377 // read command line arguments
380 // read up to arguments
381 while (nPos
< nCount
)
385 rtl_getAppCommandArg(nPos
, &arg
.pData
);
393 if (!(readOption( &aImplName
, "c", &nPos
, arg
) ||
394 readOption( &aLocation
, "l", &nPos
, arg
) ||
395 readOption( &aServiceName
, "s", &nPos
, arg
) ||
396 readOption( &aUnoUrl
, "u", &nPos
, arg
) ||
397 readOption( &s_quiet
, "quiet", &nPos
, arg
) ||
398 readOption( &bSingleAccept
, "singleaccept", &nPos
, arg
) ||
399 readOption( &bSingleInstance
, "singleinstance", &nPos
, arg
)))
401 throw RuntimeException(
402 "unexpected argument \"" + arg
+ "\"" );
406 if (!(aImplName
.isEmpty() || aServiceName
.isEmpty()))
407 throw RuntimeException("give component exOR service name!" );
408 if (aImplName
.isEmpty() && aServiceName
.isEmpty())
410 if (! aUnoUrl
.endsWithIgnoreAsciiCase( ";uno.ComponentContext" ))
411 throw RuntimeException(
412 "expected UNO-URL with instance name uno.ComponentContext!" );
414 throw RuntimeException(
415 "unexpected option --singleinstance!" );
417 if (!aImplName
.isEmpty() && aLocation
.isEmpty())
418 throw RuntimeException("give component location!" );
419 if (!aServiceName
.isEmpty() && !aLocation
.isEmpty())
420 out( "\n> warning: service name given, will ignore location!" );
422 // read component params
423 aParams
.realloc( nCount
- nPos
);
424 OUString
* pParams
= aParams
.getArray();
426 sal_uInt32 nOffset
= nPos
;
427 for ( ; nPos
< nCount
; ++nPos
)
429 rtl_getAppCommandArg( nPos
, &pParams
[nPos
-nOffset
].pData
);
432 xContext
= defaultBootstrap_InitialComponentContext();
434 // accept, instantiate, etc.
436 if (!aUnoUrl
.isEmpty()) // accepting connections
438 if (aUnoUrl
.getLength() < 10 || !aUnoUrl
.startsWithIgnoreAsciiCase( "uno:" ))
440 throw RuntimeException("illegal uno url given!" );
443 sal_Int32 nIndex
= 4; // skip initial "uno:"
444 bool bTooFewTokens
{false};
445 const OUString aConnectDescr
{ aUnoUrl
.getToken( 0, ';', nIndex
) }; // uno:CONNECTDESCR;iiop;InstanceName
446 if (nIndex
<0) bTooFewTokens
= true;
447 const OUString aUnoUrlToken
{ aUnoUrl
.getToken( 0, ';', nIndex
) };
448 if (nIndex
<0) bTooFewTokens
= true;
449 const OUString aInstanceName
{ aUnoUrl
.getToken( 0, ';', nIndex
) };
451 // Exactly 3 tokens are required
452 if (bTooFewTokens
|| nIndex
>0)
454 throw RuntimeException("illegal uno url given!" );
457 Reference
< XAcceptor
> xAcceptor
= Acceptor::create(xContext
);
460 Sequence
< Any
> aInitParams( aParams
.getLength() );
461 const OUString
* p
= aParams
.getConstArray();
462 Any
* pInitParams
= aInitParams
.getArray();
463 for ( sal_Int32 i
= aParams
.getLength(); i
--; )
465 pInitParams
[i
] <<= p
[i
];
469 Reference
< XInstanceProvider
> xInstanceProvider( new OInstanceProvider(
470 xContext
, aImplName
, aLocation
, aServiceName
, aInitParams
,
471 bSingleInstance
, aInstanceName
) );
473 // coverity[loop_top] - not really an infinite loop, we can be instructed to exit via the connection
477 out( "\n> accepting " );
478 out( aConnectDescr
);
480 Reference
< XConnection
> xConnection( xAcceptor
->accept( aConnectDescr
) );
481 out( "connection established." );
483 Reference
< XBridgeFactory
> xBridgeFactory
;
485 xBridgeFactory
, xContext
,
486 "com.sun.star.bridge.BridgeFactory" );
489 Reference
< XBridge
> xBridge( xBridgeFactory
->createBridge(
490 OUString(), aUnoUrlToken
,
491 xConnection
, xInstanceProvider
) );
495 Reference
< XComponent
> xComp( xBridge
, UNO_QUERY_THROW
);
496 ODisposingListener::waitFor( xComp
);
498 // explicitly dispose the remote bridge so that it joins
499 // on all spawned threads before process exit (see
500 // binaryurp/source/bridge.cxx for details)
507 Reference
< XInterface
> xInstance
;
508 if (!aImplName
.isEmpty()) // manually via loader
509 xInstance
= loadComponent( xContext
, aImplName
, aLocation
);
510 else // via service manager
511 createInstance( xInstance
, xContext
, aServiceName
);
514 Reference
< XMain
> xMain( xInstance
, UNO_QUERY
);
517 nRet
= xMain
->run( aParams
);
521 Reference
< XComponent
> xComp( xInstance
, UNO_QUERY
);
524 throw RuntimeException( "component does not export interface \"com.sun.star.lang.XMain\"!" );
528 catch (Exception
& rExc
)
530 out( "\n> error: " );
532 out( "\n> dying..." );
537 Reference
< XComponent
> xComp( xContext
, UNO_QUERY
);
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */