blob: c898d0d3ce3497371ce1f721b5d5e7272d43ad7c [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.brooklyn.core.config.internal;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.apache.brooklyn.api.mgmt.ExecutionContext;
import org.apache.brooklyn.api.mgmt.TaskAdaptable;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.config.SubElementConfigKey;
import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.core.task.ValueResolver;
import org.apache.brooklyn.util.text.Identifiers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;
public abstract class AbstractCollectionConfigKey<T, RawT extends Collection<Object>, V> extends AbstractStructuredConfigKey<T, RawT, V> {
private static final long serialVersionUID = 8225955960120637643L;
private static final Logger log = LoggerFactory.getLogger(AbstractCollectionConfigKey.class);
protected AbstractCollectionConfigKey(BasicConfigKey.Builder<T,?> builder, TypeToken<V> subType) {
super(builder, subType);
}
protected AbstractCollectionConfigKey(TypeToken<T> type, TypeToken<V> subType, String name, String description, T defaultValue) {
super(type, subType, name, description, defaultValue);
}
public ConfigKey<V> subKey() {
String subName = Identifiers.makeRandomId(8);
return new SubElementConfigKey<V>(this, getSubTypeToken(), getName()+"."+subName, "element of "+getName()+", uid "+subName, null);
}
protected abstract RawT merge(boolean unmodifiable, Iterable<?> ...items);
@Override
protected RawT merge(RawT base, Map<String, Object> subkeys, boolean unmodifiable) {
return merge(unmodifiable, base, subkeys.values());
}
@Override
protected RawT extractValueMatchingThisKey(Object potentialBase, ExecutionContext exec, boolean coerce) throws InterruptedException, ExecutionException {
if (coerce) {
potentialBase = resolveValue(potentialBase, exec);
}
if (potentialBase==null) return null;
if (potentialBase instanceof Map<?,?>) {
return merge(false, ((Map<?,?>) potentialBase).values() );
} else if (potentialBase instanceof Collection<?>) {
return merge(false, (Collection<?>) potentialBase );
}
log.warn("Unable to extract "+getName()+" as Collection; it is "+potentialBase.getClass().getName()+" "+potentialBase);
return null;
}
@SuppressWarnings({ "rawtypes" })
@Override
public Object applyValueToMap(Object value, Map target) {
return applyValueToMap(value, target, false);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected Object applyValueToMap(Object value, Map target, boolean isInCollection) {
if (value instanceof StructuredModification) {
return ((StructuredModification)value).applyToKeyInMap(this, target);
} else if ((value instanceof Iterable) && (!isInCollection)) {
// collections set _here_ (not in subkeys) get added
boolean isSet = isSet(target);
if (isSet) {
String warning = "Discouraged undecorated setting of a collection to in-use StructuredConfigKey "+this+": use SetModification.{set,add}. " +
"Defaulting to 'add'. Look at debug logging for call stack.";
log.warn(warning);
if (log.isDebugEnabled())
log.debug("Trace for: "+warning, new Throwable("Trace for: "+warning));
}
Iterable<?> valueI = (Iterable<?>)value;
for (Object v: valueI) {
// don't continue to recurse into these collections, however
applyValueToMap(v, target, true);
}
if (Iterables.isEmpty(valueI) && !isSet) {
target.put(this, MutableSet.of());
}
return null;
}
if (ValueResolver.isDeferredOrTaskInternal(value)) {
boolean isSet = isSet(target);
if (isSet) {
String warning = "Discouraged undecorated setting of a task/deferred to in-use StructuredConfigKey "+this+": use SetModification.{set,add}. " +
"Defaulting to replacing root. Look at debug logging for call stack.";
log.warn(warning);
if (log.isDebugEnabled())
log.debug("Trace for: "+warning, new Throwable("Trace for: "+warning));
}
}
if (isInCollection) {
// just add to set, using anonymous key
target.put(subKey(), value);
return null;
} else {
// just put here as the set - prior to 2021-05 we put things under an anonymous subkey
return target.put(this, value);
}
}
}