/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.controller.registry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.dmr.ModelNode;
/**
* Abstract {@code Resource} implementation.
*
* <p>Concurrency note: this class is *not* thread safe</p>
*
* @author Emanuel Muckenhuber
*/
public abstract class AbstractModelResource extends ResourceProvider.ResourceProviderRegistry implements Resource {
/** The children. */
private final Map<String, ResourceProvider> children = new LinkedHashMap<String, ResourceProvider>();
private final boolean runtimeOnly;
private final Set<String> orderedChildTypes;
protected AbstractModelResource() {
this(false);
}
protected AbstractModelResource(boolean runtimeOnly) {
this(runtimeOnly, (Set<String>)null);
}
protected AbstractModelResource(boolean runtimeOnly, String...orderedChildTypes) {
this(runtimeOnly, arrayToSet(orderedChildTypes));
}
protected AbstractModelResource(boolean runtimeOnly, Set<String> orderedChildTypes) {
this.runtimeOnly = runtimeOnly;
this.orderedChildTypes = orderedChildTypes == null || orderedChildTypes.size() == 0 ? Collections.<String>emptySet() : orderedChildTypes;
}
private static Set<String> arrayToSet(String[] array) {
Set<String> set;
if (array.length == 0) {
set = Collections.emptySet();
} else {
set = new HashSet<String>();
for (String type : array) {
set.add(type);
}
}
return set;
}
@Override
public Resource getChild(final PathElement address) {
final ResourceProvider provider = getProvider(address.getKey());
if(provider == null) {
return null;
}
return provider.get(address.getValue());
}
@Override
public boolean hasChild(final PathElement address) {
final ResourceProvider provider = getProvider(address.getKey());
if(provider == null) {
return false;
}
if(address.isWildcard()) {
return provider.hasChildren();
}
return provider.has(address.getValue());
}
@Override
public Resource requireChild(final PathElement address) {
final Resource resource = getChild(address);
if(resource == null) {
throw new NoSuchResourceException(address);
}
return resource;
}
@Override
public boolean hasChildren(final String childType) {
final ResourceProvider provider = getProvider(childType);
return provider != null && provider.hasChildren();
}
@Override
public Resource navigate(final PathAddress address) {
return Tools.navigate(this, address);
}
@Override
public Set<String> getChildrenNames(final String childType) {
final ResourceProvider provider = getProvider(childType);
if(provider == null) {
return Collections.emptySet();
}
return provider.children();
}
@Override
public Set<String> getChildTypes() {
synchronized (children) {
return new LinkedHashSet<String>(children.keySet());
}
}
@Override
public Set<ResourceEntry> getChildren(final String childType) {
final ResourceProvider provider = getProvider(childType);
if(provider == null) {
return Collections.emptySet();
}
final Set<ResourceEntry> children = new LinkedHashSet<ResourceEntry>();
for(final String name : provider.children()) {
final Resource resource = provider.get(name);
children.add(new DelegateResource(resource) {
@Override
public String getName() {
return name;
}
@Override
public PathElement getPathElement() {
return PathElement.pathElement(childType, name);
}
});
}
return children;
}
@Override
public void registerChild(final PathElement address, final Resource resource) {
if(address.isMultiTarget()) {
throw new IllegalArgumentException();
}
getOrCreateProvider(address.getKey()).register(address.getValue(), resource);
}
@Override
public void registerChild(final PathElement address, final int index, final Resource resource) {
if(address.isMultiTarget()) {
throw new IllegalArgumentException();
}
if (index >= 0 && !orderedChildTypes.contains(address.getKey())) {
throw ControllerLogger.ROOT_LOGGER.indexedChildResourceRegistrationNotAvailable(address);
}
getOrCreateProvider(address.getKey()).register(address.getValue(), index, resource);
}
@Override
public Resource removeChild(PathElement address) {
synchronized (children) {
final ResourceProvider provider = getProvider(address.getKey());
if(provider == null) {
return null;
}
final Resource removed = provider.remove(address.getValue());
// Cleanup default resource providers
if ((provider instanceof DefaultResourceProvider) && !provider.hasChildren()) {
children.remove(address.getKey());
}
return removed;
}
}
@Override
public boolean isProxy() {
return false;
}
@Override
public boolean isRuntime() {
return runtimeOnly;
}
@Override
public Set<String> getOrderedChildTypes() {
return orderedChildTypes.size() == 0 ? orderedChildTypes : Collections.unmodifiableSet(orderedChildTypes);
}
protected void registerResourceProvider(final String type, final ResourceProvider provider) {
synchronized (children) {
if (children.containsKey(type)) {
throw ControllerLogger.ROOT_LOGGER.duplicateResourceType(type);
}
children.put(type, provider);
}
}
protected final ResourceProvider getProvider(final String type) {
synchronized (children) {
return children.get(type);
}
}
protected ResourceProvider getOrCreateProvider(final String type) {
synchronized (children) {
final ResourceProvider provider = children.get(type);
if(provider != null) {
return provider;
} else {
final ResourceProvider newProvider = new DefaultResourceProvider();
children.put(type, newProvider);
return newProvider;
}
}
}
@Override
public abstract Resource clone();
protected void cloneProviders(AbstractModelResource clone) {
synchronized (children) {
for (final Map.Entry<String, ResourceProvider> entry : children.entrySet()) {
clone.registerResourceProvider(entry.getKey(), entry.getValue().clone());
}
}
}
private static class DefaultResourceProvider implements ResourceProvider {
private final Map<String, Resource> children = new LinkedHashMap<String, Resource>();
protected DefaultResourceProvider() {
}
@Override
public Set<String> children() {
synchronized (children) {
return new LinkedHashSet<String>(children.keySet());
}
}
@Override
public boolean has(String name) {
synchronized (children) {
return children.get(name) != null;
}
}
@Override
public Resource get(String name) {
synchronized (children) {
return children.get(name);
}
}
@Override
public boolean hasChildren() {
return ! children().isEmpty();
}
@Override
public void register(String name, Resource resource) {
synchronized (children) {
if (children.containsKey(name)) {
throw ControllerLogger.ROOT_LOGGER.duplicateResource(name);
}
children.put(name, resource);
}
}
@Override
public void register(String name, int index, Resource resource) {
synchronized (children) {
if (children.containsKey(name)) {
throw ControllerLogger.ROOT_LOGGER.duplicateResource(name);
}
if (index < 0 || index >= children.size()) {
children.put(name, resource);
} else {
List<Map.Entry<String, Resource>> list = new ArrayList<Map.Entry<String,Resource>>(children.entrySet());
children.clear();
boolean done = false;
int i = 0;
for (Map.Entry<String, Resource> entry : list) {
if (!done) {
if (i++ < index) {
children.put(entry.getKey(), entry.getValue());
} else {
children.put(name, resource);
children.put(entry.getKey(), entry.getValue());
done = true;
}
} else {
children.put(entry.getKey(), entry.getValue());
}
}
}
}
}
@Override
public Resource remove(String name) {
synchronized (children) {
return children.remove(name);
}
}
@Override
public ResourceProvider clone() {
final DefaultResourceProvider provider = new DefaultResourceProvider();
synchronized (children) {
for (final Map.Entry<String, Resource> entry : children.entrySet()) {
provider.register(entry.getKey(), entry.getValue().clone());
}
}
return provider;
}
}
abstract static class DelegateResource implements ResourceEntry {
final Resource delegate;
protected DelegateResource(Resource delegate) {
if(delegate == null) {
throw new IllegalArgumentException();
}
this.delegate = delegate;
}
@Override
public Resource getChild(PathElement element) {
return delegate.getChild(element);
}
@Override
public Set<ResourceEntry> getChildren(String childType) {
return delegate.getChildren(childType);
}
@Override
public Set<String> getChildrenNames(String childType) {
return delegate.getChildrenNames(childType);
}
@Override
public Set<String> getChildTypes() {
return delegate.getChildTypes();
}
@Override
public ModelNode getModel() {
return delegate.getModel();
}
@Override
public boolean hasChild(PathElement element) {
return delegate.hasChild(element);
}
@Override
public boolean hasChildren(String childType) {
return delegate.hasChildren(childType);
}
@Override
public boolean isModelDefined() {
return delegate.isModelDefined();
}
@Override
public Resource navigate(PathAddress address) {
return delegate.navigate(address);
}
@Override
public void registerChild(PathElement address, Resource resource) {
delegate.registerChild(address, resource);
}
@Override
public void registerChild(PathElement address, int index, Resource resource) {
delegate.registerChild(address, index, resource);
}
@Override
public Resource removeChild(PathElement address) {
return delegate.removeChild(address);
}
@Override
public Resource requireChild(PathElement element) {
return delegate.requireChild(element);
}
@Override
public void writeModel(ModelNode newModel) {
delegate.writeModel(newModel);
}
@Override
public boolean isRuntime() {
return delegate.isRuntime();
}
@Override
public boolean isProxy() {
return delegate.isProxy();
}
public Set<String> getOrderedChildTypes() {
return delegate.getOrderedChildTypes();
}
@SuppressWarnings({"CloneDoesntCallSuperClone"})
@Override
public Resource clone() {
return delegate.clone();
}
}
}