1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_extensions.hxx"
26 
27 #include "loggerconfig.hxx"
28 
29 /** === begin UNO includes === **/
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/container/XNameContainer.hpp>
32 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
33 #include <com/sun/star/util/XChangesBatch.hpp>
34 #include <com/sun/star/logging/LogLevel.hpp>
35 #include <com/sun/star/lang/NullPointerException.hpp>
36 #include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
37 #include <com/sun/star/beans/NamedValue.hpp>
38 #include <com/sun/star/logging/XLogHandler.hpp>
39 #include <com/sun/star/logging/XLogFormatter.hpp>
40 /** === end UNO includes === **/
41 
42 #include <tools/diagnose_ex.h>
43 
44 #include <comphelper/componentcontext.hxx>
45 
46 #include <cppuhelper/component_context.hxx>
47 
48 #include <vector>
49 
50 //........................................................................
51 namespace logging
52 {
53 //........................................................................
54 
55 	/** === begin UNO using === **/
56     using ::com::sun::star::uno::Reference;
57     using ::com::sun::star::logging::XLogger;
58     using ::com::sun::star::lang::XMultiServiceFactory;
59     using ::com::sun::star::uno::Sequence;
60     using ::com::sun::star::uno::Any;
61     using ::com::sun::star::container::XNameContainer;
62     using ::com::sun::star::uno::UNO_QUERY_THROW;
63     using ::com::sun::star::lang::XSingleServiceFactory;
64     using ::com::sun::star::uno::XInterface;
65     using ::com::sun::star::util::XChangesBatch;
66     using ::com::sun::star::uno::makeAny;
67     using ::com::sun::star::lang::NullPointerException;
68     using ::com::sun::star::uno::Exception;
69     using ::com::sun::star::lang::ServiceNotRegisteredException;
70     using ::com::sun::star::beans::NamedValue;
71     using ::com::sun::star::logging::XLogHandler;
72     using ::com::sun::star::logging::XLogFormatter;
73     using ::com::sun::star::container::XNameAccess;
74     using ::com::sun::star::uno::XComponentContext;
75 	/** === end UNO using === **/
76     namespace LogLevel = ::com::sun::star::logging::LogLevel;
77 
78     namespace
79     {
80 	    //----------------------------------------------------------------
81         typedef void (*SettingTranslation)( const Reference< XLogger >&, const ::rtl::OUString&, Any& );
82 
83         //----------------------------------------------------------------
84         void    lcl_substituteFileHandlerURLVariables_nothrow( const Reference< XLogger >& _rxLogger, ::rtl::OUString& _inout_rFileURL )
85         {
86             struct Variable
87             {
88                 const sal_Char*         pVariablePattern;
89                 const sal_Int32         nPatternLength;
90                 rtl_TextEncoding        eEncoding;
91                 const ::rtl::OUString   sVariableValue;
92 
93                 Variable( const sal_Char* _pVariablePattern,  const sal_Int32 _nPatternLength, rtl_TextEncoding _eEncoding,
94                         const ::rtl::OUString& _rVariableValue )
95                     :pVariablePattern( _pVariablePattern )
96                     ,nPatternLength( _nPatternLength )
97                     ,eEncoding( _eEncoding )
98                     ,sVariableValue( _rVariableValue )
99                 {
100                 }
101             };
102 
103             ::rtl::OUString sLoggerName;
104             try { sLoggerName = _rxLogger->getName(); }
105             catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
106 
107             Variable aVariables[] =
108             {
109                 Variable( RTL_CONSTASCII_USTRINGPARAM( "$(loggername)" ), sLoggerName )
110             };
111 
112             for ( size_t i = 0; i < sizeof( aVariables ) / sizeof( aVariables[0] ); ++i )
113             {
114                 ::rtl::OUString sPattern( aVariables[i].pVariablePattern, aVariables[i].nPatternLength, aVariables[i].eEncoding );
115                 sal_Int32 nVariableIndex = _inout_rFileURL.indexOf( sPattern );
116                 if  (   ( nVariableIndex == 0 )
117                     ||  (   ( nVariableIndex > 0 )
118                         &&  ( sPattern[ nVariableIndex - 1 ] != '$' )
119                         )
120                     )
121                 {
122                     // found an (unescaped) variable
123                     _inout_rFileURL = _inout_rFileURL.replaceAt( nVariableIndex, sPattern.getLength(), aVariables[i].sVariableValue );
124                 }
125             }
126         }
127 
128         //----------------------------------------------------------------
129         void    lcl_transformFileHandlerSettings_nothrow( const Reference< XLogger >& _rxLogger, const ::rtl::OUString& _rSettingName, Any& _inout_rSettingValue )
130         {
131             if ( !_rSettingName.equalsAscii( "FileURL" ) )
132                 // not interested in this setting
133                 return;
134 
135             ::rtl::OUString sURL;
136             OSL_VERIFY( _inout_rSettingValue >>= sURL );
137             lcl_substituteFileHandlerURLVariables_nothrow( _rxLogger, sURL );
138             _inout_rSettingValue <<= sURL;
139         }
140 
141 	    //----------------------------------------------------------------
142         Reference< XInterface > lcl_createInstanceFromSetting_throw(
143                 const ::comphelper::ComponentContext& _rContext,
144                 const Reference< XLogger >& _rxLogger,
145                 const Reference< XNameAccess >& _rxLoggerSettings,
146                 const sal_Char* _pServiceNameAsciiNodeName,
147                 const sal_Char* _pServiceSettingsAsciiNodeName,
148                 SettingTranslation _pSettingTranslation = NULL
149             )
150         {
151             Reference< XInterface > xInstance;
152 
153             // read the settings for the to-be-created service
154             Reference< XNameAccess > xServiceSettingsNode( _rxLoggerSettings->getByName(
155                 ::rtl::OUString::createFromAscii( _pServiceSettingsAsciiNodeName ) ), UNO_QUERY_THROW );
156 
157             Sequence< ::rtl::OUString > aSettingNames( xServiceSettingsNode->getElementNames() );
158             size_t nServiceSettingCount( aSettingNames.getLength() );
159             Sequence< NamedValue > aSettings( nServiceSettingCount );
160             if ( nServiceSettingCount )
161             {
162                 const ::rtl::OUString* pSettingNames = aSettingNames.getConstArray();
163                 const ::rtl::OUString* pSettingNamesEnd = aSettingNames.getConstArray() + aSettingNames.getLength();
164                 NamedValue* pSetting = aSettings.getArray();
165 
166                 for (   ;
167                         pSettingNames != pSettingNamesEnd;
168                         ++pSettingNames, ++pSetting
169                     )
170                 {
171                     pSetting->Name = *pSettingNames;
172                     pSetting->Value = xServiceSettingsNode->getByName( *pSettingNames );
173 
174                     if ( _pSettingTranslation )
175                         (_pSettingTranslation)( _rxLogger, pSetting->Name, pSetting->Value );
176                 }
177             }
178 
179             ::rtl::OUString sServiceName;
180             _rxLoggerSettings->getByName( ::rtl::OUString::createFromAscii( _pServiceNameAsciiNodeName ) ) >>= sServiceName;
181             if ( sServiceName.getLength() )
182             {
183                 bool bSuccess = false;
184                 if ( aSettings.getLength() )
185                 {
186                     Sequence< Any > aConstructionArgs(1);
187                     aConstructionArgs[0] <<= aSettings;
188                     bSuccess = _rContext.createComponentWithArguments( sServiceName, aConstructionArgs, xInstance );
189                 }
190                 else
191                 {
192                     bSuccess = _rContext.createComponent( sServiceName, xInstance );
193                 }
194 
195                 if ( !bSuccess )
196                     throw ServiceNotRegisteredException( sServiceName, NULL );
197             }
198 
199             return xInstance;
200         }
201     }
202 
203 	//--------------------------------------------------------------------
204     void initializeLoggerFromConfiguration( const ::comphelper::ComponentContext& _rContext, const Reference< XLogger >& _rxLogger )
205     {
206         try
207         {
208             if ( !_rxLogger.is() )
209                 throw NullPointerException();
210 
211             // the configuration provider
212             Reference< XMultiServiceFactory > xConfigProvider;
213             ::rtl::OUString sConfigProvServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationProvider" ) );
214             if ( !_rContext.createComponent( sConfigProvServiceName, xConfigProvider ) )
215                 throw ServiceNotRegisteredException( sConfigProvServiceName, _rxLogger );
216 
217             // write access to the "Settings" node (which includes settings for all loggers)
218             Sequence< Any > aArguments(1);
219             aArguments[0] <<= NamedValue(
220                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) ),
221                 makeAny( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Logging/Settings" ) ) )
222             );
223             Reference< XNameContainer > xAllSettings( xConfigProvider->createInstanceWithArguments(
224                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationUpdateAccess" ) ),
225                 aArguments
226             ), UNO_QUERY_THROW );
227 
228             ::rtl::OUString sLoggerName( _rxLogger->getName() );
229             if ( !xAllSettings->hasByName( sLoggerName ) )
230             {
231                 // no node yet for this logger. Create default settings.
232                 Reference< XSingleServiceFactory > xNodeFactory( xAllSettings, UNO_QUERY_THROW );
233                 Reference< XInterface > xLoggerSettings( xNodeFactory->createInstance(), UNO_QUERY_THROW );
234                 xAllSettings->insertByName( sLoggerName, makeAny( xLoggerSettings ) );
235                 Reference< XChangesBatch > xChanges( xAllSettings, UNO_QUERY_THROW );
236                 xChanges->commitChanges();
237             }
238 
239             // actually read and forward the settings
240             Reference< XNameAccess > xLoggerSettings( xAllSettings->getByName( sLoggerName ), UNO_QUERY_THROW );
241 
242             // the log level
243             sal_Int32 nLogLevel( LogLevel::OFF );
244             OSL_VERIFY( xLoggerSettings->getByName( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LogLevel" ) ) ) >>= nLogLevel );
245             _rxLogger->setLevel( nLogLevel );
246 
247             // the default handler, if any
248             Reference< XInterface > xUntyped( lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultHandler", "HandlerSettings", &lcl_transformFileHandlerSettings_nothrow ) );
249             if ( !xUntyped.is() )
250                 // no handler -> we're done
251                 return;
252             Reference< XLogHandler > xHandler( xUntyped, UNO_QUERY_THROW );
253             _rxLogger->addLogHandler( xHandler );
254 
255             // The newly created handler might have an own (default) level. Ensure that it uses
256             // the same level as the logger.
257             xHandler->setLevel( nLogLevel );
258 
259             // the default formatter for the handler
260             xUntyped = lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultFormatter", "FormatterSettings" );
261             if ( !xUntyped.is() )
262                 // no formatter -> we're done
263                 return;
264             Reference< XLogFormatter > xFormatter( xUntyped, UNO_QUERY_THROW );
265             xHandler->setFormatter( xFormatter );
266 
267             // TODO: we could first create the formatter, then the handler. This would allow
268             // passing the formatter as value in the component context, so the handler would
269             // not create an own default formatter
270         }
271         catch( const Exception& )
272         {
273         	DBG_UNHANDLED_EXCEPTION();
274         }
275     }
276 
277 //........................................................................
278 } // namespace logging
279 //........................................................................
280