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