blob: 47373e877ec2240568780cce1825845244d0a0e1 [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.openejb.config.provider;
import org.apache.openejb.config.sys.ServiceProvider;
import org.apache.openejb.util.SuperProperties;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A few principles guide this new implementation of ServiceProvider tracking
*
* 1. Never expose the raw datastructure underneath so providers
* must be registered via the "front door" and can't be slipped
* in without proper parent provider resolution.
*
* 2. How to locate providers from disk or parse xml
* is completely abstracted from this implementation.
*
*
* @version $Rev$ $Date$
*/
public class ProviderManager {
private final List<String> namespaces = new LinkedList<String>();
private final Map<ID, ServiceProvider> providers = new LinkedHashMap<ID, ServiceProvider>();
private final ProviderLoader loader;
public ProviderManager(ProviderLoader loader) {
this.loader = loader;
}
public ServiceProvider get(String namespace, String name) {
final ID id = new ID(namespace, name);
return getProvider(id, new LinkedHashSet<ID>());
}
public List<ServiceProvider> getAll() {
return new ArrayList<ServiceProvider>(providers.values());
}
public void register(String namespace, ServiceProvider provider) {
if (provider == null) throw new IllegalArgumentException("provider cannot be null");
final ID id = new ID(namespace, provider.getId());
register(id, provider, new LinkedHashSet<ID>());
}
public List<ServiceProvider> load(String namespace) {
if (namespace == null) throw new IllegalArgumentException("namespace cannot be null");
namespace = namespace.toLowerCase();
if (!namespaces.contains(namespace)) {
namespaces.add(namespace);
{ // load
final ArrayList<ServiceProvider> list = new ArrayList<ServiceProvider>(loader.load(namespace));
for (ServiceProvider provider : list) {
register(namespace, provider);
}
}
}
final List<ServiceProvider> providers = new ArrayList<ServiceProvider>();
for (Map.Entry<ID, ServiceProvider> entry : this.providers.entrySet()) {
if (entry.getKey().getNamespace().equals(namespace)) {
providers.add(entry.getValue());
}
}
return providers;
}
private void register(ID id, ServiceProvider provider, Set<ID> seen) {
if (providers.containsKey(id)) return;
if (provider.getParent() != null) {
final ID parentId = ID.parse(provider.getParent(), id);
// Pass in a stack on this call to prevent circular reference
final ServiceProvider parent = getProvider(parentId, seen);
if (parent == null) {
throw new NoSuchParentProviderException(provider, parentId);
}
inherit(provider, parent);
}
validate(id, provider);
providers.put(id, provider);
}
private void inherit(ServiceProvider child, ServiceProvider parent) {
if (n(child.getClassName())) child.setClassName(parent.getClassName());
if (n(child.getConstructor())) child.setConstructor(parent.getConstructor());
if (n(child.getFactoryName())) child.setFactoryName(parent.getFactoryName());
if (n(child.getDescription())) child.setDescription(parent.getDescription());
if (n(child.getDisplayName())) child.setDisplayName(parent.getDisplayName());
if (n(child.getService())) child.setService(parent.getService());
{ // types
final Set<String> types = new HashSet<String>();
types.addAll(parent.getTypes());
types.addAll(child.getTypes());
child.getTypes().clear();
child.getTypes().addAll(types);
}
{ // properties
final SuperProperties properties = new SuperProperties();
properties.putAll(parent.getProperties());
properties.putAll(child.getProperties());
child.getProperties().clear();
child.getProperties().putAll(properties);
}
}
private boolean n(String s) {
return s == null || s.length() == 0;
}
private ServiceProvider getProvider(ID id, final Set<ID> seen) {
if (seen.contains(id)) throw new ProviderCircularReferenceException(seen);
seen.add(id);
{ // Already loaded?
final ServiceProvider provider = providers.get(id);
if (provider != null) return provider;
}
{ // Can we load it?
final ServiceProvider provider = loader.load(id);
if (provider != null) {
register(id, provider, seen);
return provider;
}
}
return null;
}
private void validate(ID id, ServiceProvider provider) {
id.validate();
if (provider.getService() == null) {
throw new InvalidProviderDeclarationException("'service' attribute cannot be null", id, provider);
}
// TODO - validate provider
}
public ProviderLoader getLoader() {
return loader;
}
}