/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.service.servicemanager.configuration; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; class ServiceBindingsBuilder { // ServiceBindingsBuilder public interface public void bind(String interfaceName, String className, ClassLoader classLoader) { ServiceBinding binding = defineIfNecessary(interfaceName, classLoader); if (binding.isLocked()) { throw new ServiceConfigurationException(interfaceName + " is locked"); } binding.setImplementingClass(className); } public Collection<ServiceBinding> getAllBindings(boolean strict) { markSectionEnd(); Collection<ServiceBinding> all = new ArrayList<>(bindings.values()); for (Iterator<ServiceBinding> iter = all.iterator(); iter.hasNext(); ) { ServiceBinding binding = iter.next(); if (binding.isDirectlyRequired() && (binding.getImplementingClassName() == null)) { if (strict) throw new ServiceConfigurationException(binding.getInterfaceName() + " is required but not bound"); else iter.remove(); } } return all; } public List<String> getPriorities() { return priorities; } public void lock(String interfaceName) { require(interfaceName).lock(); } public void markDirectlyRequired(String interfaceName) { defineIfNecessary(interfaceName, null).markDirectlyRequired(); } public void mustBeBound(String interfaceName) { ServiceBinding binding = bindings.get(interfaceName); if ( (binding == null || binding.getImplementingClassName() == null) && !sectionRequirements.containsKey(interfaceName) ) { sectionRequirements.put(interfaceName, false); } } public void mustBeLocked(String interfaceName) { ServiceBinding binding = bindings.get(interfaceName); if (binding == null || !binding.isLocked()) { sectionRequirements.put(interfaceName, true); } } public void prioritize(String interfaceName) { priorities.add(interfaceName); } public void markSectionEnd() { for (Map.Entry<String,Boolean> entry : sectionRequirements.entrySet()) { String interfaceName = entry.getKey(); boolean lockRequired = entry.getValue(); ServiceBinding binding = require(interfaceName); assert binding.getImplementingClassName() != null; // require makes this check if ( lockRequired && (!binding.isLocked()) ) { throw new ServiceConfigurationException(binding.getImplementingClassName() + " is not locked"); } } sectionRequirements.clear(); } public void unbind(String interfaceName) { ServiceBinding binding = bindings.remove(interfaceName); if (binding != null) { if (binding.isLocked()) { throw new ServiceConfigurationException("interface " + interfaceName + " is locked and cannot be unbound"); } } } // for testing void bind(String interfaceName, String className) { bind(interfaceName, className, null); } // private methods private ServiceBinding defineIfNecessary(String interfaceName, ClassLoader classLoader) { ServiceBinding binding = bindings.get(interfaceName); if (binding == null) { binding = new ServiceBinding(interfaceName); binding.setClassLoader(classLoader); bindings.put(interfaceName, binding); } else { if (binding.isLocked()) { if (binding.getClassLoader() != classLoader) throw new ServiceConfigurationException("interface " + interfaceName + " is locked, but bound to two ClassLoaders"); } else { binding.setClassLoader(classLoader); } } return binding; } private ServiceBinding require(String interfaceName) { ServiceBinding binding = bindings.get(interfaceName); if (binding == null || binding.getImplementingClassName() == null) { throw new ServiceConfigurationException(interfaceName + " is not defined"); } return binding; } // object state // invariant: key = bindings[key].getInterfaceName() private final Map<String, ServiceBinding> bindings = new HashMap<>(); /** * This defines section requirements as a map of interface_name -> boolean. All interface names in this map * must be bound by the end of the current section; iff the entry's value is true, the binding must also be locked. * If we ever need to track more requirements, we should change the Boolean to an enum set. */ private final Map<String,Boolean> sectionRequirements = new HashMap<>(); /** Interfaces that ought to be run first (in order), as permitted * by dependencies. */ private final List<String> priorities = new ArrayList<>(); }