/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.activemq.plugin; import org.apache.activemq.util.IntrospectionSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.bind.JAXBElement; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.regex.Pattern; import org.apache.activemq.schema.core.DtoBroker; public class DefaultConfigurationProcessor implements ConfigurationProcessor { public static final Logger LOG = LoggerFactory.getLogger(DefaultConfigurationProcessor.class); RuntimeConfigurationBroker plugin; Class configurationClass; Pattern matchPassword = Pattern.compile("password=.*,"); public DefaultConfigurationProcessor(RuntimeConfigurationBroker plugin, Class configurationClass) { this.plugin = plugin; this.configurationClass = configurationClass; } @Override public void processChanges(DtoBroker currentConfiguration, DtoBroker modifiedConfiguration) { List current = filter(currentConfiguration, configurationClass); List modified = filter(modifiedConfiguration, configurationClass); if (current.equals(modified)) { plugin.debug("no changes to " + configurationClass.getSimpleName()); return; } else { plugin.info("changes to " + configurationClass.getSimpleName()); } processChanges(current, modified); } public void processChanges(List current, List modified) { int modIndex = 0, currentIndex = 0; for (; modIndex < modified.size() && currentIndex < current.size(); modIndex++, currentIndex++) { // walk the list for mods applyModifications(getContents(current.get(currentIndex)), getContents(modified.get(modIndex))); } for (; modIndex < modified.size(); modIndex++) { // new element; add all for (Object nc : getContents(modified.get(modIndex))) { ConfigurationProcessor processor = findProcessor(nc); if (processor != null) { processor.addNew(nc); } else { addNew(nc); } } } for (; currentIndex < current.size(); currentIndex++) { // removal of element; remove all for (Object nc : getContents(current.get(currentIndex))) { ConfigurationProcessor processor = findProcessor(nc); if (processor != null) { processor.remove(nc); } else { remove(nc); } } } } protected void applyModifications(List<Object> current, List<Object> modification) { int modIndex = 0, currentIndex = 0; for (; modIndex < modification.size() && currentIndex < current.size(); modIndex++, currentIndex++) { Object existing = current.get(currentIndex); Object candidate = modification.get(modIndex); if (!existing.equals(candidate)) { plugin.debug("modification to:" + existing + " , with: " + candidate); ConfigurationProcessor processor = findProcessor(existing); if (processor != null) { processor.modify(existing, candidate); } else { modify(existing, candidate); } } } for (; modIndex < modification.size(); modIndex++) { Object mod = modification.get(modIndex); ConfigurationProcessor processor = findProcessor(mod); if (processor != null) { processor.addNew(mod); } else { addNew(mod); } } for (; currentIndex < current.size(); currentIndex++) { Object mod = current.get(currentIndex); ConfigurationProcessor processor = findProcessor(mod); if (processor != null) { processor.remove(mod); } else { remove(mod); } } } public void modify(Object existing, Object candidate) { remove(existing); addNew(candidate); } public void addNew(Object o) { plugin.info("No runtime support for additions of " + o); } public void remove(Object o) { plugin.info("No runtime support for removal of: " + o); } @Override public ConfigurationProcessor findProcessor(Object o) { plugin.info("No processor for " + o); return null; } // mapping all supported updatable elements to support getContents protected List<Object> getContents(Object o) { List<Object> answer = new ArrayList<Object>(); try { Object val = o.getClass().getMethod("getContents", new Class[]{}).invoke(o, new Object[]{}); if (val instanceof List) { answer = (List<Object>) val; } else { answer.add(val); } } catch (NoSuchMethodException mappingIncomplete) { plugin.debug(filterPasswords(o) + " has no modifiable elements"); } catch (Exception e) { plugin.info("Failed to access getContents for " + o + ", runtime modifications not supported", e); } return answer; } protected String filterPasswords(Object toEscape) { return matchPassword.matcher(toEscape.toString()).replaceAll("password=???,"); } protected <T> List<Object> filter(Object obj, Class<T> type) { return filter(getContents(obj), type); } protected <T> List<Object> filter(List<Object> objectList, Class<T> type) { List<Object> result = new LinkedList<Object>(); for (Object o : objectList) { if (o instanceof JAXBElement) { JAXBElement element = (JAXBElement) o; if (type.isAssignableFrom(element.getDeclaredType())) { result.add((T) element.getValue()); } } else if (type.isAssignableFrom(o.getClass())) { result.add((T) o); } } return result; } protected <T> T fromDto(Object dto, T instance) { Properties properties = new Properties(); IntrospectionSupport.getProperties(dto, properties, null); plugin.placeHolderUtil.filter(properties); LOG.trace("applying props: " + filterPasswords(properties) + ", to " + instance.getClass().getSimpleName()); IntrospectionSupport.setProperties(instance, properties); // deal with nested elements for (Object nested : filter(dto, Object.class)) { String elementName = nested.getClass().getSimpleName(); Method setter = JAXBUtils.findSetter(instance, elementName); if (setter != null) { List<Object> argument = new LinkedList<Object>(); for (Object elementContent : filter(nested, Object.class)) { argument.add(fromDto(elementContent, JAXBUtils.inferTargetObject(elementContent))); } try { setter.invoke(instance, JAXBUtils.matchType(argument, setter.getParameterTypes()[0])); } catch (Exception e) { plugin.info("failed to invoke " + setter + " on " + instance, e); } } else { plugin.info("failed to find setter for " + elementName + " on :" + instance); } } return instance; } }