All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.inteligr8.alfresco.attrclean.AbstractBootstrapService Maven / Gradle / Ivy

/*
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see .
 */
package com.inteligr8.alfresco.attrclean;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;

public abstract class AbstractBootstrapService extends AbstractLifecycleBean implements BootstrapService {
	
	public enum Scope {
		None,
		JMX,
		ShardRegistry,
		Custom;
		
		static Scope caseInsensitiveValueOf(String value) {
			for (Scope scope : Scope.values())
				if (scope.toString().equalsIgnoreCase(value))
					return scope;
			return null;
		}
	}
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	private final Pattern rootKeyPattern = Pattern.compile("(.+)/.*");
	
	@Autowired
	@Qualifier("attributeService")
	private AttributeService attributeService;
	
	@Value("${inteligr8.attrcleaner.enabled}")
	private boolean enabled;
	
	@Value("${inteligr8.attrcleaner.log-level}")
	private String logLevelRaw;
	
	protected Level logLevel;
	protected Scope scope;
	protected Map> keyPatterns;
	
	protected abstract String getRawScope();
	
	protected abstract String getRawKeys();
	
	@Override
	protected void onBootstrap(ApplicationEvent aevent) {
		if (!this.enabled) {
			this.logger.info("Inteligr8 Attribute Cleaner module is disabled");
			return;
		}

		this.logger.info("Inteligr8 Attribute Cleaner {} bootstrapping", this.getClass().getSimpleName());
		
		if (this.validateAndNormalize())
			this.execute();
	}
	
	@Override
	protected void onShutdown(ApplicationEvent aevent) {
		// do nothing
	}

	@Override
	public boolean validateAndNormalize() {
		this.logLevel = Level.valueOf(this.logLevelRaw.toUpperCase());
		PropertyCheck.mandatory(this, "inteligr8.attrcleaner.log-level", this.logLevel);
		
		this.scope = Scope.caseInsensitiveValueOf(this.getRawScope().replace("-", ""));
		this.logger.trace("Attribute cleaner {} scope is {}", this.getClass().getSimpleName(), this.scope);
		if (this.scope == null)
			throw new IllegalArgumentException();
		
		if (Scope.None.equals(this.scope)) {
			this.logger.debug("Attribute cleaner {} feature is off", this.getClass().getSimpleName());
			return false;
		}
		
		this.keyPatterns = new LinkedHashMap<>();
		
		for (String keyRaw : this.getRawKeys().split(",")) {
			this.logger.trace("Attribute cleaner {} key: {}", this.getClass().getSimpleName(), keyRaw);
			
			if (keyRaw.length() == 0) {
				this.logger.debug("Skipping empty key");
				continue;
			}
			
			Matcher matcher = this.rootKeyPattern.matcher(keyRaw);
			if (!matcher.find()) {
				this.logger.warn("Key must have a root element; skipping: {}", keyRaw);
				continue;
			}
			
			String rootKey = matcher.group(1).replace("\\.", ".");
			
			Pattern keyPattern = Pattern.compile(keyRaw);
			this.logger.debug("Validated key pattern: {} => {}", rootKey, keyPattern);
			this.putAddToList(this.keyPatterns, rootKey, keyPattern);
		}
		
		return true;
	}
	
	@Override
	public void execute() {
		Map attributes = null;
		
		switch (this.scope) {
			case None:
				throw new IllegalArgumentException();
			case JMX:
				attributes = this.queryJmx();
				break;
			case ShardRegistry:
				attributes = this.queryShardRegistry();
				break;
			case Custom:
				attributes = this.queryCustom();
				break;
			default:
				throw new IllegalArgumentException();
		}
		
		this.logger.atLevel(this.logLevel).log("Queried {} Attributes: ", attributes.size());
		for (Entry entry : attributes.entrySet()) {
			this.execute(entry);
		}
	}
	
	public abstract void execute(Entry entry);
	
	private Map queryJmx() {
		return this.queryAttrs(".PropertyBackedBeans");
	}
	
	private Map queryShardRegistry() {
		Map attributes = new LinkedHashMap<>();
		attributes.putAll(this.queryAttrs(".SHARD_STATE"));
		attributes.putAll(this.queryAttrs(".SHARD_SUBSCRIPTION"));
		return attributes;
	}
	
	private Map queryCustom() {
		Map attributes = new LinkedHashMap<>();
		
		AttributeQueryCallback aqc = new AttributeQueryCallback() {
			@Override
			public boolean handleAttribute(Long attrId, Serializable value, Serializable[] keys) {
				String keysAsStr = StringUtils.stripEnd(StringUtils.join(keys, "/"), "/");
				for (Entry> patterns : keyPatterns.entrySet()) {
					for (Pattern pattern : patterns.getValue()) {
						if (pattern.matcher(keysAsStr).matches()) {
							logger.debug("{} matches attribute: {}", pattern, keysAsStr);
							attributes.put(keys, value);
						} else {
							logger.trace("{} does not match attribute: {}", pattern, keysAsStr);
						}
					}
				}
				
				return true;
			}
		};
		
		for (String rootKey : this.keyPatterns.keySet()) {
			this.logger.debug("Querying for attributes with root key: {}", rootKey);
			this.attributeService.getAttributes(aqc, rootKey);
		}
		
		return attributes;
	}
	
	private Map queryAttrs(Serializable... selectKeys) {
		Map attributes = new LinkedHashMap<>();
		
		AttributeQueryCallback aqc = new AttributeQueryCallback() {
			@Override
			public boolean handleAttribute(Long attrId, Serializable value, Serializable[] keys) {
				logger.trace("Found attribute: {}", Arrays.toString(keys));
				attributes.put(keys, value);
				return true;
			}
		};
		
		this.logger.debug("Querying for attributes with keys: {}", Arrays.toString(selectKeys));
		this.attributeService.getAttributes(aqc, selectKeys);
		
		return attributes;
	}
	
	@SuppressWarnings("unchecked")
	private , V> void putAddToList(Map map, K key, V value) {
		CV c = map.get(key);
		if (c == null) {
			c = (CV) new LinkedList();
			map.put(key, c);
		}
		c.add(value);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy