/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.core.logging; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.EnvUtil; public class LoggingRegistry { private static LoggingRegistry registry = new LoggingRegistry(); private Map<String, LoggingObjectInterface> map; private Map<String, LogChannelFileWriterBuffer> fileWriterBuffers; private Map<String, List<String>> childrenMap; private Date lastModificationTime; private int maxSize; private final int DEFAULT_MAX_SIZE = 10000; private Object syncObject = new Object(); private LoggingRegistry() { this.map = new ConcurrentHashMap<String, LoggingObjectInterface>(); this.childrenMap = new ConcurrentHashMap<String, List<String>>(); this.fileWriterBuffers = new ConcurrentHashMap<>(); this.lastModificationTime = new Date(); this.maxSize = Const.toInt( EnvUtil.getSystemProperty( "KETTLE_MAX_LOGGING_REGISTRY_SIZE" ), DEFAULT_MAX_SIZE ); } public static LoggingRegistry getInstance() { return registry; } public String registerLoggingSource( Object object ) { synchronized ( this.syncObject ) { this.maxSize = Const.toInt( EnvUtil.getSystemProperty( "KETTLE_MAX_LOGGING_REGISTRY_SIZE" ), 10000 ); LoggingObject loggingSource = new LoggingObject( object ); LoggingObjectInterface found = findExistingLoggingSource( loggingSource ); if ( found != null ) { LoggingObjectInterface foundParent = found.getParent(); LoggingObjectInterface loggingSourceParent = loggingSource.getParent(); if ( foundParent != null && loggingSourceParent != null ) { String foundParentLogChannelId = foundParent.getLogChannelId(); String sourceParentLogChannelId = loggingSourceParent.getLogChannelId(); if ( foundParentLogChannelId != null && sourceParentLogChannelId != null && foundParentLogChannelId.equals( sourceParentLogChannelId ) ) { String foundLogChannelId = found.getLogChannelId(); if ( foundLogChannelId != null ) { return foundLogChannelId; } } } } String logChannelId = UUID.randomUUID().toString(); loggingSource.setLogChannelId( logChannelId ); this.map.put( logChannelId, loggingSource ); if ( loggingSource.getParent() != null ) { String parentLogChannelId = loggingSource.getParent().getLogChannelId(); if ( parentLogChannelId != null ) { List<String> parentChildren = this.childrenMap.get( parentLogChannelId ); if ( parentChildren == null ) { parentChildren = new ArrayList<String>(); this.childrenMap.put( parentLogChannelId, parentChildren ); } parentChildren.add( logChannelId ); } } this.lastModificationTime = new Date(); loggingSource.setRegistrationDate( this.lastModificationTime ); if ( ( this.maxSize > 0 ) && ( this.map.size() > this.maxSize ) ) { List<LoggingObjectInterface> all = new ArrayList<LoggingObjectInterface>( this.map.values() ); Collections.sort( all, new Comparator<LoggingObjectInterface>() { @Override public int compare( LoggingObjectInterface o1, LoggingObjectInterface o2 ) { if ( ( o1 == null ) && ( o2 != null ) ) { return -1; } if ( ( o1 != null ) && ( o2 == null ) ) { return 1; } if ( ( o1 == null ) && ( o2 == null ) ) { return 0; } if ( o1.getRegistrationDate() == null && o2.getRegistrationDate() != null ) { return -1; } if ( o1.getRegistrationDate() != null && o2.getRegistrationDate() == null ) { return 1; } if ( o1.getRegistrationDate() == null && o2.getRegistrationDate() == null ) { return 0; } return ( o1.getRegistrationDate().compareTo( o2.getRegistrationDate() ) ); } } ); int cutCount = this.maxSize < 1000 ? this.maxSize : 1000; List<String> channelsNotToRemove = getLogChannelFileWriterBufferIds(); for ( int i = 0; i < cutCount; i++ ) { LoggingObjectInterface toRemove = all.get( i ); if ( !channelsNotToRemove.contains( toRemove.getLogChannelId() ) ) { this.map.remove( toRemove.getLogChannelId() ); } } removeOrphans(); } return logChannelId; } } public LoggingObjectInterface findExistingLoggingSource( LoggingObjectInterface loggingObject ) { LoggingObjectInterface found = null; for ( LoggingObjectInterface verify : this.map.values() ) { if ( loggingObject.equals( verify ) ) { found = verify; break; } } return found; } public LoggingObjectInterface getLoggingObject( String logChannelId ) { return this.map.get( logChannelId ); } public Map<String, LoggingObjectInterface> getMap() { return this.map; } public List<String> getLogChannelChildren( String parentLogChannelId ) { if ( parentLogChannelId == null ) { return null; } List<String> list = getLogChannelChildren( new ArrayList<String>(), parentLogChannelId ); list.add( parentLogChannelId ); return list; } private List<String> getLogChannelChildren( List<String> children, String parentLogChannelId ) { synchronized ( this.syncObject ) { List<String> list = this.childrenMap.get( parentLogChannelId ); if ( list == null ) { // Don't do anything, just return the input. return children; } Iterator<String> kids = list.iterator(); while ( kids.hasNext() ) { String logChannelId = kids.next(); // Add the children recursively getLogChannelChildren( children, logChannelId ); // Also add the current parent children.add( logChannelId ); } } return children; } public Date getLastModificationTime() { return this.lastModificationTime; } public String dump( boolean includeGeneral ) { StringBuilder out = new StringBuilder( 50000 ); for ( LoggingObjectInterface o : this.map.values() ) { if ( ( includeGeneral ) || ( !o.getObjectType().equals( LoggingObjectType.GENERAL ) ) ) { out.append( o.getContainerObjectId() ); out.append( "\t" ); out.append( o.getLogChannelId() ); out.append( "\t" ); out.append( o.getObjectType().name() ); out.append( "\t" ); out.append( o.getObjectName() ); out.append( "\t" ); out.append( o.getParent() != null ? o.getParent().getLogChannelId() : "-" ); out.append( "\t" ); out.append( o.getParent() != null ? o.getParent().getObjectType().name() : "-" ); out.append( "\t" ); out.append( o.getParent() != null ? o.getParent().getObjectName() : "-" ); out.append( "\n" ); } } return out.toString(); } /** * For junit testing purposes * @return ro items map */ Map<String, LoggingObjectInterface> dumpItems() { return Collections.unmodifiableMap( this.map ); } /** * For junit testing purposes * @return ro parent-child relations map */ Map<String, List<String>> dumpChildren() { return Collections.unmodifiableMap( this.childrenMap ); } public void removeIncludingChildren( String logChannelId ) { synchronized ( this.map ) { List<String> children = getLogChannelChildren( logChannelId ); for ( String child : children ) { this.map.remove( child ); } this.map.remove( logChannelId ); removeOrphans(); } } public void removeOrphans() { // Remove all orphaned children this.childrenMap.keySet().retainAll( this.map.keySet() ); } public void registerLogChannelFileWriterBuffer( LogChannelFileWriterBuffer fileWriterBuffer ) { this.fileWriterBuffers.put( fileWriterBuffer.getLogChannelId(), fileWriterBuffer ); } public LogChannelFileWriterBuffer getLogChannelFileWriterBuffer( String id ) { for ( String bufferId : this.fileWriterBuffers.keySet() ) { if ( getLogChannelChildren( bufferId ).contains( id ) ) { return this.fileWriterBuffers.get( bufferId ); } } return null; } protected List<String> getLogChannelFileWriterBufferIds() { Set<String> bufferIds = this.fileWriterBuffers.keySet(); List<String> ids = new ArrayList<>(); for ( String id : bufferIds ) { ids.addAll( getLogChannelChildren( id ) ); } ids.addAll( bufferIds ); return ids; } public void removeLogChannelFileWriterBuffer( String id ) { Set<String> bufferIds = this.fileWriterBuffers.keySet(); for ( String bufferId : bufferIds ) { if ( getLogChannelChildren( id ).contains( bufferId ) ) { this.fileWriterBuffers.remove( bufferId ); } } } }