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.camel.blueprint;
018
019import java.lang.reflect.Method;
020import java.util.LinkedHashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.aries.blueprint.ext.PropertyPlaceholderExt;
026import org.apache.camel.spi.PropertiesSource;
027import org.apache.camel.support.ObjectHelper;
028import org.apache.camel.support.service.ServiceSupport;
029import org.apache.camel.util.ReflectionHelper;
030import org.osgi.service.blueprint.container.BlueprintContainer;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Blueprint {@link PropertiesSource} which supports looking up
036 * property placeholders from the Blueprint Property Placeholder Service.
037 */
038public class BlueprintPropertiesSource extends ServiceSupport implements PropertiesSource {
039
040    private static final Logger LOG = LoggerFactory.getLogger(BlueprintPropertiesSource.class);
041    private final BlueprintContainer container;
042    private final List<String> ids;
043    private final Set<PropertyPlaceholderWrapper> placeholders = new LinkedHashSet<>();
044
045    public BlueprintPropertiesSource(BlueprintContainer container, List<String> ids) {
046        this.container = container;
047        this.ids = ids;
048    }
049
050    @Override
051    public String getName() {
052        return "BlueprintPropertiesSource" + ids;
053    }
054
055    @Override
056    public String getProperty(String name) {
057        String answer = null;
058
059        for (PropertyPlaceholderWrapper placeholder : placeholders) {
060            boolean isDefault = false;
061            if (placeholders.size() > 1) {
062                // okay we have multiple placeholders and we want to return the answer that
063                // is not the default placeholder if there is multiple keys
064                Map map = placeholder.getDefaultProperties();
065                isDefault = map != null && map.containsKey(name);
066                LOG.trace("Blueprint property key: {} is part of default properties: {}", name, isDefault);
067            }
068
069            try {
070                String candidate = placeholder.retrieveValue(name);
071                if (candidate != null) {
072                    if (answer == null || !isDefault) {
073                        LOG.trace("Blueprint candidate property key: {} as value: {}", name, answer);
074                        answer = candidate;
075                    }
076                }
077            } catch (Exception ex) {
078                // Here we just catch the exception and try to use other candidate
079            }
080        }
081        LOG.debug("Blueprint getProperty: {}={}", name, answer);
082
083        return answer;
084    }
085
086    /**
087     * Adds the given Blueprint property placeholder service with the given id
088     *
089     * @param id id of the Blueprint property placeholder service to add.
090     */
091    private void addPropertyPlaceholder(String id) {
092        Object component = container.getComponentInstance(id);
093
094        if (component instanceof PropertyPlaceholderExt) {
095            Class<?> clazz = component.getClass();
096            if (clazz != null) {
097                LOG.debug("Adding Blueprint PropertyPlaceholder: {}", id);
098                Method method = ReflectionHelper.findMethod(clazz, "retrieveValue", String.class);
099                Method defaultMethod = ReflectionHelper.findMethod(clazz, "getDefaultProperties");
100                if (method != null) {
101                    method.setAccessible(true);
102                    if (defaultMethod != null) {
103                        defaultMethod.setAccessible(true);
104                    }
105                    placeholders.add(new PropertyPlaceholderWrapper(component, method, defaultMethod));
106                } else {
107                    throw new IllegalStateException("Cannot add blueprint property placeholder: " + id
108                            + " as the method retrieveValue is not found");
109                }
110            }
111        }
112    }
113
114    @Override
115    protected void doInit() throws Exception {
116        for (String id : ids) {
117            addPropertyPlaceholder(id);
118        }
119    }
120
121    @Override
122    protected void doStart() throws Exception {
123        // noop
124    }
125
126    @Override
127    protected void doStop() throws Exception {
128        // noop
129    }
130
131    private class PropertyPlaceholderWrapper {
132
133        private final Object delegate;
134        private final Method method;
135        private final Method defaultMethod;
136
137        PropertyPlaceholderWrapper(Object delegate, Method method, Method defaultMethod) {
138            this.delegate = delegate;
139            this.method = method;
140            this.defaultMethod = defaultMethod;
141        }
142
143        String retrieveValue(String key) {
144            Object v = ObjectHelper.invokeMethod(method, delegate, key);
145            return v == null ? null : v.toString();
146        }
147
148        Map getDefaultProperties() {
149            if (defaultMethod != null) {
150                return (Map) ObjectHelper.invokeMethod(defaultMethod, delegate);
151            }
152            return null;
153        }
154    }
155
156}