/* * Copyright 2011 Future Systems * * Licensed 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.krakenapps.radius.server.impl; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.krakenapps.confdb.Config; import org.krakenapps.confdb.ConfigCollection; import org.krakenapps.confdb.ConfigDatabase; import org.krakenapps.confdb.ConfigIterator; import org.krakenapps.confdb.ConfigService; import org.krakenapps.confdb.Predicates; import org.krakenapps.radius.server.RadiusFactory; import org.krakenapps.radius.server.RadiusFactoryEventListener; import org.krakenapps.radius.server.RadiusInstance; import org.krakenapps.radius.server.RadiusModule; import org.krakenapps.radius.server.RadiusModuleType; import org.krakenapps.radius.server.RadiusPortType; import org.krakenapps.radius.server.RadiusProfile; import org.krakenapps.radius.server.RadiusServer; import org.krakenapps.radius.server.RadiusServerEventListener; import org.krakenapps.radius.server.RadiusVirtualServer; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "radius-server") @Provides(specifications = { RadiusServer.class }) public class RadiusServerImpl implements RadiusServer, RadiusFactoryEventListener { public static final int DEFAULT_AUTH_PORT = 1812; private final Logger logger = LoggerFactory.getLogger(RadiusServerImpl.class.getName()); @Requires private ConfigService cfg; private BundleContext bc; private Map<String, RadiusVirtualServer> virtualServers; private Map<String, RadiusProfile> profiles; private Map<RadiusModuleType, RadiusModule> modules; private CopyOnWriteArraySet<RadiusServerEventListener> callbacks; private ExecutorService executor; public RadiusServerImpl(BundleContext bc) { this.bc = bc; } @Validate public void start() { this.callbacks = new CopyOnWriteArraySet<RadiusServerEventListener>(); this.virtualServers = new ConcurrentHashMap<String, RadiusVirtualServer>(); this.profiles = new ConcurrentHashMap<String, RadiusProfile>(); this.modules = new ConcurrentHashMap<RadiusModuleType, RadiusModule>(); this.executor = Executors.newCachedThreadPool(); loadProfiles(); loadModules(); loadVirtualServers(); } private void loadVirtualServers() { ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); ConfigIterator it = db.findAll(RadiusVirtualServerConfig.class); for (RadiusVirtualServerConfig config : it.getDocuments(RadiusVirtualServerConfig.class)) { try { if (config.getPortType() == null) { logger.warn("kraken radius: virtual server [{}]'s port type is null", config.getName()); continue; } RadiusPortType portType = RadiusPortType.parse(config.getPortType()); Integer port = determinePort(config.getPort(), portType); InetSocketAddress bindAddress = null; if (config.getHostName() != null) bindAddress = new InetSocketAddress(config.getHostName(), port); else bindAddress = new InetSocketAddress(port); RadiusVirtualServerImpl vs = new RadiusVirtualServerImpl(this, config.getName(), config.getProfile(), portType, executor, bindAddress); vs.open(); virtualServers.put(config.getName(), vs); } catch (IOException e) { logger.error("kraken radius: cannot load virtual server - " + config.getName(), e); } } } private void loadModules() { for (RadiusModuleType t : RadiusModuleType.values()) { RadiusModule module = new RadiusModule(bc, this, t, cfg); modules.put(t, module); loadModuleInstances(t); module.start(); } } private void loadModuleInstances(RadiusModuleType t) { RadiusModule module = modules.get(t); ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); ConfigCollection col = db.ensureCollection("instances"); for (Object o : col.findAll().getDocuments()) { @SuppressWarnings("unchecked") Map<String, Object> m = (Map<String, Object>) o; String name = (String) m.get("name"); module.loadInstance(name); } } private void unloadModules() { for (RadiusModuleType t : RadiusModuleType.values()) { RadiusModule module = modules.get(t); if (module == null) continue; try { module.stop(); } catch (Exception e) { logger.warn("kraken radius: module callback should not throw any exception", e); } } modules.clear(); } private void loadProfiles() { ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); for (RadiusProfile profile : db.findAll(RadiusProfile.class).getDocuments(RadiusProfile.class)) { profiles.put(profile.getName(), profile); } } private Integer determinePort(Integer port, RadiusPortType portType) { if (port == null) { if (portType == RadiusPortType.Authentication) port = RadiusVirtualServerImpl.DEFAULT_AUTH_PORT; else if (portType == RadiusPortType.Accounting) port = RadiusVirtualServerImpl.DEFAULT_ACCT_PORT; } return port; } @Invalidate public void stop() { stopVirtualServers(); unloadModules(); } private void stopVirtualServers() { for (RadiusVirtualServer vs : virtualServers.values()) { try { logger.info("kraken radius: stopping virtual server [{}] - {}", vs.getName(), vs.getBindAddress()); vs.close(); } catch (IOException e) { logger.error("kraken radius: cannot stop virtual server " + vs.getName(), e); } } virtualServers.clear(); } @Override public List<RadiusVirtualServer> getVirtualServers() { return new ArrayList<RadiusVirtualServer>(virtualServers.values()); } @Override public RadiusVirtualServer getVirtualServer(String name) { return virtualServers.get(name); } @Override public RadiusVirtualServer createVirtualServer(String name, RadiusPortType portType, String profileName) { return createVirtualServer(name, portType, profileName, null); } @Override public RadiusVirtualServer createVirtualServer(String name, RadiusPortType portType, String profileName, InetSocketAddress bindAddress) { verifyNotNull("name", name); verifyNotNull("port type", portType); verifyNotNull("profile name", profileName); if (!profiles.containsKey(profileName)) throw new IllegalArgumentException("profile not found: " + profileName); if (virtualServers.containsKey(name)) throw new IllegalStateException("duplicated virtual server name: " + name); ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); RadiusVirtualServerConfig config = new RadiusVirtualServerConfig(); config.setName(name); config.setProfile(profileName); config.setPortType(portType.getAlias()); if (bindAddress != null) { config.setPort(bindAddress.getPort()); config.setHostName(bindAddress.getAddress().getHostAddress()); } else { config.setPort(DEFAULT_AUTH_PORT); } db.add(config); RadiusVirtualServerImpl vs = new RadiusVirtualServerImpl(this, name, profileName, portType, executor, bindAddress); virtualServers.put(vs.getName(), vs); return vs; } @Override public void removeVirtualServer(String name) { verifyNotNull("name", name); RadiusVirtualServer vs = virtualServers.remove(name); if (vs == null) return; try { vs.close(); } catch (IOException e) { logger.error("kraken radius: cannot close virtual server " + name, e); } } @Override public List<RadiusProfile> getProfiles() { return new ArrayList<RadiusProfile>(profiles.values()); } @Override public RadiusProfile getProfile(String name) { verifyNotNull("name", name); return profiles.get(name); } @Override public void createProfile(RadiusProfile profile) { verifyNotNull("profile", profile); ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); Config c = db.findOne(RadiusProfile.class, Predicates.field("name", profile.getName())); if (c != null) throw new IllegalStateException("duplicated profile name: " + profile.getName()); db.add(profile); profiles.put(profile.getName(), profile); } @Override public void updateProfile(RadiusProfile profile) { verifyNotNull("profile", profile); ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); Config c = db.findOne(RadiusProfile.class, Predicates.field("name", profile.getName())); if (c == null) throw new IllegalStateException("profile not found: " + profile.getName()); db.update(c, profile); profiles.put(profile.getName(), profile); } @Override public void removeProfile(String name) { verifyNotNull("name", name); ConfigDatabase db = cfg.ensureDatabase("kraken-radius"); Config c = db.findOne(RadiusProfile.class, Predicates.field("name", name)); if (c == null) throw new IllegalStateException("profile not found: " + name); db.remove(c); profiles.remove(name); } @Override public List<RadiusModule> getModules() { return new ArrayList<RadiusModule>(modules.values()); } @Override public RadiusModule getModule(RadiusModuleType type) { return modules.get(type); } @Override public RadiusInstance getModuleInstance(RadiusModuleType type, String instanceName) { RadiusModule module = getModule(type); if (module == null) return null; return module.getInstance(instanceName); } @Override public RadiusInstance createModuleInstance(RadiusModuleType type, String instanceName, String factoryName, Map<String, Object> configs) { RadiusModule module = getModule(type); if (module == null) throw new IllegalStateException("module not found: " + type); RadiusFactory<?> factory = module.getFactory(factoryName); if (factory == null) throw new IllegalStateException("factory not found: " + factoryName); return module.createInstance(instanceName, factoryName, configs); } @Override public void removeModuleInstance(RadiusModuleType type, String instanceName) { RadiusModule module = getModule(type); if (module == null) throw new IllegalStateException("module not found: " + type); module.removeInstance(instanceName); } @Override public void addEventListener(RadiusServerEventListener listener) { callbacks.add(listener); } @Override public void removeEventListener(RadiusServerEventListener listener) { callbacks.remove(listener); } @Override public void addingService(Object service) { for (RadiusModuleType t : RadiusModuleType.values()) { if (t.getFactoryClass().isAssignableFrom(service.getClass())) { RadiusModule module = getModule(t); RadiusFactory<?> factory = (RadiusFactory<?>) service; module.addFactory(factory); loadModuleInstances(t); return; } } } @Override public void removedService(Object service) { for (RadiusModuleType t : RadiusModuleType.values()) { if (t.getFactoryClass().isAssignableFrom(service.getClass())) { RadiusFactory<?> factory = (RadiusFactory<?>) service; RadiusModule module = getModule(t); module.removeFactory(factory.getName()); return; } } } private void verifyNotNull(String name, Object value) { if (value == null) throw new IllegalArgumentException(name + " should be not null"); } }