001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.plugin; 018 019import org.apache.activemq.util.IntrospectionSupport; 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023import javax.xml.bind.JAXBElement; 024import java.lang.reflect.Method; 025import java.util.ArrayList; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Properties; 029import java.util.regex.Pattern; 030import org.apache.activemq.schema.core.DtoBroker; 031 032public class DefaultConfigurationProcessor implements ConfigurationProcessor { 033 034 public static final Logger LOG = LoggerFactory.getLogger(DefaultConfigurationProcessor.class); 035 RuntimeConfigurationBroker plugin; 036 Class configurationClass; 037 038 Pattern matchPassword = Pattern.compile("password=.*,"); 039 040 public DefaultConfigurationProcessor(RuntimeConfigurationBroker plugin, Class configurationClass) { 041 this.plugin = plugin; 042 this.configurationClass = configurationClass; 043 } 044 045 @Override 046 public void processChanges(DtoBroker currentConfiguration, DtoBroker modifiedConfiguration) { 047 List current = filter(currentConfiguration, configurationClass); 048 List modified = filter(modifiedConfiguration, configurationClass); 049 050 if (current.equals(modified)) { 051 plugin.debug("no changes to " + configurationClass.getSimpleName()); 052 return; 053 } else { 054 plugin.info("changes to " + configurationClass.getSimpleName()); 055 } 056 057 processChanges(current, modified); 058 } 059 060 public void processChanges(List current, List modified) { 061 int modIndex = 0, currentIndex = 0; 062 for (; modIndex < modified.size() && currentIndex < current.size(); modIndex++, currentIndex++) { 063 // walk the list for mods 064 applyModifications(getContents(current.get(currentIndex)), 065 getContents(modified.get(modIndex))); 066 } 067 068 for (; modIndex < modified.size(); modIndex++) { 069 // new element; add all 070 for (Object nc : getContents(modified.get(modIndex))) { 071 ConfigurationProcessor processor = findProcessor(nc); 072 if (processor != null) { 073 processor.addNew(nc); 074 } else { 075 addNew(nc); 076 } 077 } 078 } 079 080 for (; currentIndex < current.size(); currentIndex++) { 081 // removal of element; remove all 082 for (Object nc : getContents(current.get(currentIndex))) { 083 ConfigurationProcessor processor = findProcessor(nc); 084 if (processor != null) { 085 processor.remove(nc); 086 } else { 087 remove(nc); 088 } 089 } 090 } 091 } 092 093 protected void applyModifications(List<Object> current, List<Object> modification) { 094 int modIndex = 0, currentIndex = 0; 095 for (; modIndex < modification.size() && currentIndex < current.size(); modIndex++, currentIndex++) { 096 Object existing = current.get(currentIndex); 097 Object candidate = modification.get(modIndex); 098 if (!existing.equals(candidate)) { 099 plugin.debug("modification to:" + existing + " , with: " + candidate); 100 ConfigurationProcessor processor = findProcessor(existing); 101 if (processor != null) { 102 processor.modify(existing, candidate); 103 } else { 104 modify(existing, candidate); 105 } 106 } 107 } 108 for (; modIndex < modification.size(); modIndex++) { 109 Object mod = modification.get(modIndex); 110 ConfigurationProcessor processor = findProcessor(mod); 111 if (processor != null) { 112 processor.addNew(mod); 113 } else { 114 addNew(mod); 115 } 116 } 117 for (; currentIndex < current.size(); currentIndex++) { 118 Object mod = current.get(currentIndex); 119 ConfigurationProcessor processor = findProcessor(mod); 120 if (processor != null) { 121 processor.remove(mod); 122 } else { 123 remove(mod); 124 } 125 } 126 } 127 128 public void modify(Object existing, Object candidate) { 129 remove(existing); 130 addNew(candidate); 131 } 132 133 public void addNew(Object o) { 134 plugin.info("No runtime support for additions of " + o); 135 } 136 137 public void remove(Object o) { 138 plugin.info("No runtime support for removal of: " + o); 139 } 140 141 @Override 142 public ConfigurationProcessor findProcessor(Object o) { 143 plugin.info("No processor for " + o); 144 return null; 145 } 146 147 // mapping all supported updatable elements to support getContents 148 protected List<Object> getContents(Object o) { 149 List<Object> answer = new ArrayList<Object>(); 150 try { 151 Object val = o.getClass().getMethod("getContents", new Class[]{}).invoke(o, new Object[]{}); 152 if (val instanceof List) { 153 answer = (List<Object>) val; 154 } else { 155 answer.add(val); 156 } 157 } catch (NoSuchMethodException mappingIncomplete) { 158 plugin.debug(filterPasswords(o) + " has no modifiable elements"); 159 } catch (Exception e) { 160 plugin.info("Failed to access getContents for " + o + ", runtime modifications not supported", e); 161 } 162 return answer; 163 } 164 165 protected String filterPasswords(Object toEscape) { 166 return matchPassword.matcher(toEscape.toString()).replaceAll("password=???,"); 167 } 168 169 protected <T> List<Object> filter(Object obj, Class<T> type) { 170 return filter(getContents(obj), type); 171 } 172 173 protected <T> List<Object> filter(List<Object> objectList, Class<T> type) { 174 List<Object> result = new LinkedList<Object>(); 175 for (Object o : objectList) { 176 if (o instanceof JAXBElement) { 177 JAXBElement element = (JAXBElement) o; 178 if (type.isAssignableFrom(element.getDeclaredType())) { 179 result.add((T) element.getValue()); 180 } 181 } else if (type.isAssignableFrom(o.getClass())) { 182 result.add((T) o); 183 } 184 } 185 return result; 186 } 187 188 protected <T> T fromDto(Object dto, T instance) { 189 Properties properties = new Properties(); 190 IntrospectionSupport.getProperties(dto, properties, null); 191 plugin.placeHolderUtil.filter(properties); 192 LOG.trace("applying props: " + filterPasswords(properties) + ", to " + instance.getClass().getSimpleName()); 193 IntrospectionSupport.setProperties(instance, properties); 194 195 // deal with nested elements 196 for (Object nested : filter(dto, Object.class)) { 197 String elementName = nested.getClass().getSimpleName(); 198 Method setter = JAXBUtils.findSetter(instance, elementName); 199 if (setter != null) { 200 List<Object> argument = new LinkedList<Object>(); 201 for (Object elementContent : filter(nested, Object.class)) { 202 argument.add(fromDto(elementContent, JAXBUtils.inferTargetObject(elementContent))); 203 } 204 try { 205 setter.invoke(instance, JAXBUtils.matchType(argument, setter.getParameterTypes()[0])); 206 } catch (Exception e) { 207 plugin.info("failed to invoke " + setter + " on " + instance, e); 208 } 209 } else { 210 plugin.info("failed to find setter for " + elementName + " on :" + instance); 211 } 212 } 213 return instance; 214 } 215}