001    /*
002     * Copyright 2004 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.transit.monitor;
020    
021    import java.net.URL;
022    import java.util.logging.Logger;
023    import java.util.logging.Level;
024    
025    import net.dpml.lang.PID;
026    
027    /**
028     * Generic adapter that redirects monitor events to a logging channel.
029     *
030     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
031     * @version 1.0.3
032     */
033    public class LoggingAdapter implements Adapter
034    {
035        // ------------------------------------------------------------------------
036        // static
037        // ------------------------------------------------------------------------
038    
039       /**
040        * Constant kb value.
041        */
042        private static final int KBYTE = 1024;
043    
044       /**
045        * The logging channel.
046        */
047        private static final String CATEGORY = "";
048    
049        private static final PID ID = new PID();
050    
051        // ------------------------------------------------------------------------
052        // state
053        // ------------------------------------------------------------------------
054    
055        private Logger m_logger;
056    
057        // ------------------------------------------------------------------------
058        // constructor
059        // ------------------------------------------------------------------------
060    
061       /**
062        * Creation of a new console adapter that is used to redirect transit events
063        * the system output stream.
064        */
065        public LoggingAdapter()
066        {
067             this( Logger.getLogger( CATEGORY ) );
068        }
069    
070       /**
071        * Creation of a new console adapter that is used to redirect transit events
072        * the system output stream.
073        * @param logger the assigned logging channel
074        */
075        public LoggingAdapter( Logger logger )
076        {
077             m_logger = logger;
078        }
079    
080       /**
081        * Creation of a new console adapter that is used to redirect transit events
082        * the system output stream.
083        * @param category the logging channel category name
084        */
085        public LoggingAdapter( String category )
086        {
087             m_logger = Logger.getLogger( category );
088        }
089    
090        // ------------------------------------------------------------------------
091        // Adapter
092        // ------------------------------------------------------------------------
093    
094       /**
095        * Return TRUE is trace level logging is enabled.
096        * @return the enabled state of trace logging
097        */
098        public boolean isTraceEnabled()
099        {
100            return m_logger.isLoggable( Level.FINER );
101        }
102        
103       /**
104        * Return TRUE is debug level logging is enabled.
105        * @return the enabled state of debug logging
106        */
107        public boolean isDebugEnabled()
108        {
109            return m_logger.isLoggable( Level.FINE );
110        }
111    
112       /**
113        * Return TRUE is info level logging is enabled.
114        * @return the enabled state of info logging
115        */
116        public boolean isInfoEnabled()
117        {
118            return m_logger.isLoggable( Level.INFO );
119        }
120    
121       /**
122        * Return TRUE is error level logging is enabled.
123        * @return the enabled state of error logging
124        */
125        public boolean isWarnEnabled()
126        {
127            return m_logger.isLoggable( Level.WARNING );
128        }
129    
130       /**
131        * Return TRUE is error level logging is enabled.
132        * @return the enabled state of error logging
133        */
134        public boolean isErrorEnabled()
135        {
136            return m_logger.isLoggable( Level.SEVERE );
137        }
138    
139       /**
140        * Log a debug message is trace mode is enabled.
141        * @param message the message to log
142        */
143        public void trace( String message )
144        {
145            if( isTraceEnabled() )
146            {
147                m_logger.finer( message );
148            }
149        }
150    
151       /**
152        * Log a debug message is debug mode is enabled.
153        * @param message the message to log
154        */
155        public void debug( String message )
156        {
157            if( isDebugEnabled() )
158            {
159                m_logger.fine( message );
160            }
161        }
162    
163       /**
164        * Log a info level message.
165        * @param message the message to log
166        */
167        public void info( String message )
168        {
169            if( isInfoEnabled() )
170            {
171                m_logger.info( message );
172            }
173        }
174    
175       /**
176        * Record a warning message.
177        * @param message the warning message to record
178        */
179        public void warn( String message )
180        {
181            if( isWarnEnabled() )
182            {
183                m_logger.warning( message );
184            }
185        }
186    
187       /**
188        * Record a warning message.
189        * @param message the warning message to record
190        * @param cause the causal exception
191        */
192        public void warn( String message, Throwable cause )
193        {
194            if( isWarnEnabled() )
195            {
196                m_logger.log( Level.WARNING, message, cause );
197            }
198        }
199    
200       /**
201        * Log a error message.
202        * @param message the message to log
203        */
204        public void error( String message )
205        {
206            if( isErrorEnabled() )
207            {
208                m_logger.log( Level.SEVERE, message );
209            }
210        }
211    
212       /**
213        * Log a error message.
214        * @param message the message to log
215        * @param e the causal exception
216        */
217        public void error( String message, Throwable e )
218        {        
219            if( isErrorEnabled() )
220            {
221                m_logger.log( Level.SEVERE, message, e );
222            }
223        }
224    
225       /**
226        * Return a child logger.
227        * @param category the sub-category name.
228        * @return the child logging channel
229        */
230        public net.dpml.util.Logger getChildLogger( String category )
231        {
232            if( ( null == category ) || "".equals( category ) )
233            {
234                return this;
235            }
236            else
237            {
238                String name = m_logger.getName();
239                String path = trim( name + "." + category );
240                return new LoggingAdapter( Logger.getLogger( path ) );
241            }
242        }
243    
244        private String trim( String path )
245        {
246            if( path.startsWith( "." ) )
247            {
248                return trim( path.substring( 1 ) );
249            }
250            else if( ".".equals( path ) )
251            {
252                return "";
253            }
254            else
255            {
256                return path;
257            }
258        }
259    
260        /**
261         * Handle download notification.
262         * @param resource the resource under attention
263         * @param total the estimated download size
264         * @param count the progress towards expected
265         */
266        public void notify( URL resource, int total, int count )
267        {
268            String path = resource.toString();
269            if( path.startsWith( "file:" ) )
270            {
271                return;
272            }
273            if( isAnt() || ( "true".equals( System.getProperty( "dpml.subprocess" ) ) ) )
274            {
275                if( count == 0 )
276                {
277                    info( "downloading [" + resource + "] (" + getFranctionalValue( total ) + ")" );
278                }
279                return;
280            }
281            
282            if( isInfoEnabled() )
283            {
284                String max = getFranctionalValue( total );
285                String value = getFranctionalValue( count );
286                int pad = max.length() - value.length();
287                String level = getLogHeader();
288                String process = getProcessHeader();
289                StringBuffer buffer = new StringBuffer( process + level );
290                String name = path.substring( path.lastIndexOf( '/' ) + 1 );
291                buffer.append( "(" + m_logger.getName() + "): " );
292                buffer.append( "retrieving: " + name + " " );
293                for( int i=0; i < pad; i++ )
294                {
295                    buffer.append( " " );
296                }
297                buffer.append( value );
298                buffer.append( "k/" );
299                if( total == 0 )
300                {
301                    buffer.append( "?" );
302                }
303                else
304                {
305                    buffer.append( max );
306                    buffer.append( "k\r" );
307                }
308                if( total == count )
309                {
310                    System.out.println( buffer.toString() );
311                }
312                else
313                {
314                    System.out.print( buffer.toString() );
315                }
316            }
317        }
318    
319       /**
320        * Internal utility to return the locaized logging level name.
321        * @return the localized name
322        */
323        private String getLogHeader()
324        {
325            StringBuffer buffer = new StringBuffer();
326            buffer.append( "[" );
327            Level level = getLoggerLevel( m_logger );
328            buffer.append( level.getLocalizedName() );
329            buffer.append( "        " );
330            String tag = buffer.toString();
331            return tag.substring( 0, EIGHT ) + "] ";
332        }
333    
334        private String getProcessHeader()
335        {
336            StringBuffer buffer = new StringBuffer();
337            buffer.append( "[" );
338            buffer.append( ID.getValue() );
339            buffer.append( "        " );
340            String tag = buffer.toString();
341            return tag.substring( 0, PROCESS_HEADER_WIDTH ) + "] ";
342        }
343    
344    
345        private Level getLoggerLevel( Logger logger )
346        {
347            Level level = logger.getLevel();
348            if( level != null )
349            {
350                return level;
351            }
352            else
353            {
354                Logger parent = logger.getParent();
355                if( null != parent )
356                {
357                    return getLoggerLevel( parent );
358                }
359                else
360                {
361                    final String error = 
362                      "Logging level is not defined.";
363                    throw new IllegalStateException( error );
364                }
365            }
366        }
367    
368        // ------------------------------------------------------------------------
369        // implementation
370        // ------------------------------------------------------------------------
371    
372       /**
373        * Test is the runtime environment is ant.
374        * @return true if ant is visible
375        */
376        private boolean isAnt()
377        {
378            return null != System.getProperty( "ant.home" );
379            /*
380            try
381            {
382                ClassLoader.getSystemClassLoader().loadClass( "org.apache.tools.ant.launch.AntMain" );
383                return true;
384            }
385            catch( Exception e )
386            {
387                return false;
388            }
389            */
390        }
391    
392       /**
393        * Return a string representing the number of kilobytes relative to the supplied
394        * total bytes.
395        * @param total the byte value
396        * @return the string to log
397        */
398        private static String getFranctionalValue( int total )
399        {
400            final int offset = 3;
401    
402            float realTotal = new Float( total ).floatValue();
403            float realK = new Float( KBYTE ).floatValue();
404            float r = ( realTotal / realK );
405    
406            String value = new Float( r ).toString();
407            int j = value.indexOf( "." );
408            if( j > -1 )
409            {
410                 int q = value.length();
411                 int k = q - j;
412                 if( k > offset )
413                 {
414                     return value.substring( 0, j + offset );
415                 }
416                 else
417                 {
418                     return value;
419                 }
420            }
421            else
422            {
423                 return value;
424            }
425        }
426    
427       /**
428        * Internal utility to log a message to system.out.
429        * @param message the message to log
430        */
431        private void log( String message )
432        {
433            m_logger.fine( message );
434        }
435    
436        private static final int EIGHT = 8;
437        private static final int PROCESS_HEADER_WIDTH = 6;
438    }
439