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 */ 017 package org.apache.camel.blueprint; 018 019 import java.lang.reflect.Method; 020 import java.util.ArrayList; 021 import java.util.LinkedHashSet; 022 import java.util.List; 023 import java.util.Properties; 024 import java.util.Set; 025 026 import org.apache.aries.blueprint.ExtendedBeanMetadata; 027 import org.apache.aries.blueprint.ext.AbstractPropertyPlaceholder; 028 import org.apache.camel.component.properties.DefaultPropertiesParser; 029 import org.apache.camel.component.properties.PropertiesComponent; 030 import org.apache.camel.component.properties.PropertiesParser; 031 import org.apache.camel.util.ObjectHelper; 032 import org.osgi.service.blueprint.container.BlueprintContainer; 033 import org.osgi.service.blueprint.reflect.ComponentMetadata; 034 035 /** 036 * Blueprint {@link PropertiesParser} which supports looking up 037 * property placeholders from the Blueprint Property Placeholder Service. 038 * <p/> 039 * This implementation will sit on top of any existing configured 040 * {@link PropertiesParser} and will delegate to those in case Blueprint could not 041 * resolve the property. 042 */ 043 public class BlueprintPropertiesParser extends DefaultPropertiesParser { 044 045 private final PropertiesComponent propertiesComponent; 046 private final BlueprintContainer container; 047 private final PropertiesParser delegate; 048 private final Set<AbstractPropertyPlaceholder> placeholders = new LinkedHashSet<AbstractPropertyPlaceholder>(); 049 private Method method; 050 051 public BlueprintPropertiesParser(PropertiesComponent propertiesComponent, BlueprintContainer container, PropertiesParser delegate) { 052 this.propertiesComponent = propertiesComponent; 053 this.container = container; 054 this.delegate = delegate; 055 } 056 057 /** 058 * Lookup the ids of the Blueprint property placeholder services in the 059 * Blueprint container. 060 * 061 * @return the ids, will be an empty array if none found. 062 */ 063 public String[] lookupPropertyPlaceholderIds() { 064 List<String> ids = new ArrayList<String>(); 065 066 for (Object componentId : container.getComponentIds()) { 067 String id = (String) componentId; 068 ComponentMetadata meta = container.getComponentMetadata(id); 069 if (meta instanceof ExtendedBeanMetadata) { 070 Class<?> clazz = ((ExtendedBeanMetadata) meta).getRuntimeClass(); 071 if (clazz != null && AbstractPropertyPlaceholder.class.isAssignableFrom(clazz)) { 072 ids.add(id); 073 } 074 } 075 } 076 077 return ids.toArray(new String[ids.size()]); 078 } 079 080 /** 081 * Adds the given Blueprint property placeholder service with the given id 082 * 083 * @param id id of the Blueprint property placeholder service to add. 084 */ 085 public void addPropertyPlaceholder(String id) { 086 Object component = container.getComponentInstance(id); 087 088 if (component instanceof AbstractPropertyPlaceholder) { 089 AbstractPropertyPlaceholder placeholder = (AbstractPropertyPlaceholder) component; 090 placeholders.add(placeholder); 091 092 log.debug("Adding Blueprint PropertyPlaceholder: {}", id); 093 094 if (method == null) { 095 try { 096 method = AbstractPropertyPlaceholder.class.getDeclaredMethod("getProperty", String.class); 097 method.setAccessible(true); 098 } catch (NoSuchMethodException e) { 099 throw new IllegalStateException("Cannot add blueprint property placeholder: " + id 100 + " as the method getProperty is not accessible", e); 101 } 102 } 103 } 104 } 105 106 @Override 107 public String parseProperty(String key, String value, Properties properties) { 108 log.trace("Parsing property key: {} with value: {}", key, value); 109 110 String answer = null; 111 112 // prefer any override properties 113 // this logic is special for BlueprintPropertiesParser as we otherwise prefer 114 // to use the AbstractPropertyPlaceholder from OSGi blueprint config admins 115 // service to lookup otherwise 116 if (key != null && propertiesComponent.getOverrideProperties() != null) { 117 answer = (String) propertiesComponent.getOverrideProperties().get(key); 118 } 119 120 // lookup key in blueprint and return its value 121 if (answer == null && key != null) { 122 for (AbstractPropertyPlaceholder placeholder : placeholders) { 123 answer = (String) ObjectHelper.invokeMethod(method, placeholder, key); 124 if (answer != null) { 125 log.debug("Blueprint parsed property key: {} as value: {}", key, answer); 126 break; 127 } 128 } 129 } 130 131 // if there is a delegate then let it parse the current answer as it may be jasypt which 132 // need to decrypt values 133 if (delegate != null) { 134 String delegateAnswer = delegate.parseProperty(key, answer != null ? answer : value, properties); 135 if (delegateAnswer != null) { 136 answer = delegateAnswer; 137 } 138 } 139 140 log.trace("Returning parsed property key: {} as value: {}", key, answer); 141 return answer; 142 } 143 144 }