blob: ae2f8123c63f0a1d8956ca08f0a747fc6fcf8c0d [file] [log] [blame]
/**
* 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.aries.blueprint.ext;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.aries.blueprint.ComponentDefinitionRegistry;
import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
import org.apache.aries.blueprint.PassThroughMetadata;
import org.apache.aries.blueprint.mutable.MutableBeanArgument;
import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
import org.apache.aries.blueprint.mutable.MutableBeanProperty;
import org.apache.aries.blueprint.mutable.MutableCollectionMetadata;
import org.apache.aries.blueprint.mutable.MutableMapEntry;
import org.apache.aries.blueprint.mutable.MutableMapMetadata;
import org.apache.aries.blueprint.mutable.MutablePropsMetadata;
import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
import org.osgi.service.blueprint.container.ComponentDefinitionException;
import org.osgi.service.blueprint.reflect.BeanArgument;
import org.osgi.service.blueprint.reflect.BeanMetadata;
import org.osgi.service.blueprint.reflect.BeanProperty;
import org.osgi.service.blueprint.reflect.CollectionMetadata;
import org.osgi.service.blueprint.reflect.MapEntry;
import org.osgi.service.blueprint.reflect.MapMetadata;
import org.osgi.service.blueprint.reflect.Metadata;
import org.osgi.service.blueprint.reflect.NonNullMetadata;
import org.osgi.service.blueprint.reflect.PropsMetadata;
import org.osgi.service.blueprint.reflect.Target;
import org.osgi.service.blueprint.reflect.ValueMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract class for property placeholders.
*
* @version $Rev: 1211548 $, $Date: 2011-12-07 17:26:22 +0000 (Wed, 07 Dec 2011) $
*/
public abstract class AbstractPropertyPlaceholder implements ComponentDefinitionRegistryProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPropertyPlaceholder.class);
private ExtendedBlueprintContainer blueprintContainer;
private String placeholderPrefix = "${";
private String placeholderSuffix = "}";
private Pattern pattern;
private LinkedList<String> processingStack = new LinkedList<String>();
public String getPlaceholderPrefix() {
return placeholderPrefix;
}
public void setPlaceholderPrefix(String placeholderPrefix) {
this.placeholderPrefix = placeholderPrefix;
}
public String getPlaceholderSuffix() {
return placeholderSuffix;
}
public void setPlaceholderSuffix(String placeholderSuffix) {
this.placeholderSuffix = placeholderSuffix;
}
public ExtendedBlueprintContainer getBlueprintContainer() {
return blueprintContainer;
}
public void setBlueprintContainer(ExtendedBlueprintContainer blueprintContainer) {
this.blueprintContainer = blueprintContainer;
}
public void process(ComponentDefinitionRegistry registry) throws ComponentDefinitionException {
try {
for (String name : registry.getComponentDefinitionNames()) {
processMetadata(registry.getComponentDefinition(name));
}
} finally {
processingStack.clear();
}
}
protected Metadata processMetadata(Metadata metadata) {
try {
if (metadata instanceof BeanMetadata) {
BeanMetadata bmd = (BeanMetadata) metadata;
processingStack.add("Bean named " + bmd.getId() + "->");
return processBeanMetadata(bmd);
} else if (metadata instanceof CollectionMetadata) {
CollectionMetadata cmd = (CollectionMetadata) metadata;
processingStack.add("Collection of type " + cmd.getCollectionClass() + "->");
return processCollectionMetadata(cmd);
} else if (metadata instanceof MapMetadata) {
processingStack.add("Map->");
return processMapMetadata((MapMetadata) metadata);
} else if (metadata instanceof PropsMetadata) {
processingStack.add("Properties->");
return processPropsMetadata((PropsMetadata) metadata);
} else if (metadata instanceof ValueMetadata) {
processingStack.add("Value->");
return processValueMetadata((ValueMetadata) metadata);
} else {
processingStack.add("Unknown Metadata " + metadata + "->");
return metadata;
}
} finally {
processingStack.removeLast();
}
}
protected Metadata processBeanMetadata(BeanMetadata component) {
for (BeanArgument arg : component.getArguments()) {
try {
processingStack.add(
"Argument index " + arg.getIndex() + " and value type " + arg.getValueType() + "->");
if(arg instanceof MutableBeanArgument) {
((MutableBeanArgument) arg).setValue(processMetadata(arg.getValue()));
} else {
//Say that we can't change this argument, but continue processing
//If the value is mutable then we may be ok!
printWarning(arg, "Constructor Argument");
processMetadata(arg.getValue());
}
} finally {
processingStack.removeLast();
}
}
for (BeanProperty prop : component.getProperties()) {
try {
processingStack.add("Property named " + prop.getName() + "->");
if(prop instanceof MutableBeanProperty) {
((MutableBeanProperty) prop).setValue(processMetadata(prop.getValue()));
} else {
//Say that we can't change this property, but continue processing
//If the value is mutable then we may be ok!
printWarning(prop, "Injection Property");
processMetadata(prop.getValue());
}
} finally {
processingStack.removeLast();
}
}
Target factoryComponent = component.getFactoryComponent();
if(factoryComponent != null) {
try {
if(component instanceof MutableBeanMetadata) {
processingStack.add("Factory Component->");
((MutableBeanMetadata) component).setFactoryComponent(
(Target) processMetadata(factoryComponent));
} else {
printWarning(component, "Factory Component");
processingStack.add("Factory Component->");
processMetadata(factoryComponent);
}
} finally {
processingStack.removeLast();
}
}
return component;
}
protected Metadata processPropsMetadata(PropsMetadata metadata) {
List<MapEntry> entries = new ArrayList<MapEntry>(metadata.getEntries());
if(!!! entries.isEmpty()) {
try {
if(metadata instanceof MutablePropsMetadata) {
processingStack.add("Properties->");
MutablePropsMetadata mpm = (MutablePropsMetadata) metadata;
for (MapEntry entry : entries) {
mpm.removeEntry(entry);
}
for (MapEntry entry : processMapEntries(entries)) {
mpm.addEntry(entry);
}
} else {
printWarning(metadata, "Properties");
processingStack.add("Properties->");
processMapEntries(entries);
}
} finally {
processingStack.removeLast();
}
}
return metadata;
}
protected Metadata processMapMetadata(MapMetadata metadata) {
List<MapEntry> entries = new ArrayList<MapEntry>(metadata.getEntries());
if(!!! entries.isEmpty()) {
try {
if(metadata instanceof MutableMapMetadata) {
processingStack.add("Map->");
MutableMapMetadata mmm = (MutableMapMetadata) metadata;
for (MapEntry entry : entries) {
mmm.removeEntry(entry);
}
for (MapEntry entry : processMapEntries(entries)) {
mmm.addEntry(entry);
}
} else {
printWarning(metadata, "Map");
processingStack.add("Map->");
processMapEntries(entries);
}
} finally {
processingStack.removeLast();
}
}
return metadata;
}
protected List<MapEntry> processMapEntries(List<MapEntry> entries) {
for (MapEntry entry : entries) {
try {
processingStack.add("Map Entry Key: " + entry.getKey() + " Value: " + entry.getValue() + "->" );
if(entry instanceof MutableMapEntry) {
((MutableMapEntry) entry).setKey((NonNullMetadata) processMetadata(entry.getKey()));
((MutableMapEntry) entry).setValue(processMetadata(entry.getValue()));
} else {
printWarning(entry, "Map Entry");
processMetadata(entry.getKey());
processMetadata(entry.getValue());
}
} finally {
processingStack.removeLast();
}
}
return entries;
}
protected Metadata processCollectionMetadata(CollectionMetadata metadata) {
List<Metadata> values = new ArrayList<Metadata>(metadata.getValues());
if(!!! values.isEmpty()) {
try {
if(metadata instanceof MutableCollectionMetadata) {
processingStack.add("Collection type: " + metadata.getValueType() + "->");
MutableCollectionMetadata mcm = (MutableCollectionMetadata) metadata;
for (Metadata value : values) {
mcm.removeValue(value);
}
for (Metadata value : values) {
mcm.addValue(processMetadata(value));
}
} else {
printWarning(metadata, "Collection type: " + metadata.getValueType());
processingStack.add("Collection type: " + metadata.getValueType() + "->");
for (Metadata value : values) {
processMetadata(value);
}
}
} finally {
processingStack.removeLast();
}
}
return metadata;
}
protected Metadata processValueMetadata(ValueMetadata metadata) {
return new LateBindingValueMetadata(metadata);
}
private void printWarning(Object immutable, String processingType) {
StringBuilder sb = new StringBuilder("The property placeholder processor for ");
sb.append(placeholderPrefix).append(',').append(" ").append(placeholderSuffix)
.append(" found an immutable ").append(processingType)
.append(" at location ");
for(String s : processingStack) {
sb.append(s);
}
sb.append(". This may prevent properties, beans, or other items referenced by this component from being properly processed.");
LOGGER.info(sb.toString());
}
protected String retrieveValue(String expression) {
return getProperty(expression);
}
protected String processString(String str) {
// TODO: we need to handle escapes on the prefix / suffix
Matcher matcher = getPattern().matcher(str);
while (matcher.find()) {
String rep = retrieveValue(matcher.group(1));
if (rep != null) {
str = str.replace(matcher.group(0), rep);
matcher.reset(str);
}
}
return str;
}
protected String getProperty(String val) {
return null;
}
protected Pattern getPattern() {
if (pattern == null) {
pattern = Pattern.compile("\\Q" + placeholderPrefix + "\\E(.+?)\\Q" + placeholderSuffix + "\\E");
}
return pattern;
}
public class LateBindingValueMetadata implements ValueMetadata {
private final ValueMetadata metadata;
private boolean retrieved;
private String retrievedValue;
public LateBindingValueMetadata(ValueMetadata metadata) {
this.metadata = metadata;
}
public String getStringValue() {
if (!retrieved) {
String v = metadata.getStringValue();
LOGGER.debug("Before process: {}", v);
retrievedValue = processString(v);
LOGGER.debug("After process: {}", retrievedValue);
retrieved = true;
}
return retrievedValue;
}
public String getType() {
return metadata.getType();
}
}
}