/* * TeleStax, Open Source Cloud Communications * Copyright 2011-2016, TeleStax Inc. and individual contributors * by the @authors tag. * * This program is free software: you can redistribute it and/or modify * 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/> * * This file incorporates work covered by the following copyright and * permission notice: * * JBoss, Home of Professional Open Source * Copyright 2007-2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt 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.jdiameter.client.impl.controller; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.jdiameter.api.ApplicationId; import org.jdiameter.api.Avp; import org.jdiameter.api.InternalException; import org.jdiameter.api.LocalAction; import org.jdiameter.api.Realm; import org.jdiameter.api.Statistic; import org.jdiameter.client.api.IAnswer; import org.jdiameter.client.api.IAssembler; import org.jdiameter.client.api.IContainer; import org.jdiameter.client.api.IMessage; import org.jdiameter.client.api.IRequest; import org.jdiameter.client.api.controller.IRealm; import org.jdiameter.client.api.controller.IRealmTable; import org.jdiameter.server.api.agent.IAgent; import org.jdiameter.server.api.agent.IAgentConfiguration; import org.jdiameter.server.api.agent.IProxy; import org.jdiameter.server.api.agent.IRedirect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a> * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a> */ public class RealmTableImpl implements IRealmTable { private static final Logger logger = LoggerFactory.getLogger(RealmTableImpl.class); // maps name->realms (cause there might be more than one realm defined, with different app id. protected Map<String, RealmSet> realmNameToRealmSet = new HashMap<String, RealmSet>(); // "cache" so we don't have to combine all realms protected List<String> allRealmsSet = new ArrayList<String>(); protected String localRealmName; protected String localHost; protected IAssembler assembler; public RealmTableImpl(IContainer con) { this.assembler = con.getAssemblerFacility(); } @Override public boolean realmExists(String realmName) { // NOTE: this is still valid for local realm return (this.realmNameToRealmSet.containsKey(realmName) && this.realmNameToRealmSet.get(realmName).size() > 0); } @Override public Realm addRealm(String realmName, ApplicationId applicationId, LocalAction action, String agentConfiguration, boolean dynamic, long expirationTime, String[] hosts) throws InternalException { logger.debug("Adding realm [{}] into network map", realmName); IAgentConfiguration agentConf = this.assembler.getComponentInstance(IAgentConfiguration.class); if (agentConf != null) { agentConf = agentConf.parse(agentConfiguration); } return addRealm(realmName, applicationId, action, agentConf, dynamic, expirationTime, hosts); } @Override public Realm addRealm(String realmName, ApplicationId applicationId, LocalAction action, IAgentConfiguration agentConf, boolean dynamic, long expirationTime, String[] hosts) throws InternalException { IAgent agent = null; switch (action) { case LOCAL: case RELAY: break; case PROXY: agent = this.assembler.getComponentInstance(IProxy.class); break; case REDIRECT: agent = this.assembler.getComponentInstance(IRedirect.class); break; } RealmImpl realmImpl = new RealmImpl(realmName, applicationId, action, agent, agentConf, dynamic, expirationTime, hosts); addRealm(realmImpl); return realmImpl; } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#getRealm(java.lang.String, org.jdiameter.api.ApplicationId) */ @Override public Realm getRealm(String realmName, ApplicationId applicationId) { RealmSet rs = this.realmNameToRealmSet.get(realmName); return rs == null ? null : rs.getRealm(applicationId); } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#removeRealmApplicationId(java.lang.String, org.jdiameter.api.ApplicationId) */ @Override public Realm removeRealmApplicationId(String realmName, ApplicationId appId) { RealmSet set = this.realmNameToRealmSet.get(realmName); if (set != null) { Realm r = set.getRealm(appId); set.removeRealm(appId); if (set.size() == 0 && !realmName.equals(this.localRealmName)) { this.realmNameToRealmSet.remove(realmName); this.allRealmsSet.remove(realmName); } return r; } return null; } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#removeRealms(java.lang.String) */ @Override public Collection<Realm> removeRealm(String realmName) { RealmSet set = null; if (realmName.equals(this.localRealmName)) { set = this.realmNameToRealmSet.get(realmName); } else { set = this.realmNameToRealmSet.remove(realmName); if (set != null) { Collection<Realm> present = set.values(); allRealmsSet.remove(realmName); return new ArrayList<Realm>(present); } } return null; } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#getRealms(java.lang.String) */ @Override public Collection<Realm> getRealms(String realmName) { RealmSet set = this.realmNameToRealmSet.get(realmName); if (set != null) { Collection<Realm> present = set.values(); return new ArrayList<Realm>(present); } return new ArrayList<Realm>(0); } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#getRealms() */ @Override public Collection<Realm> getRealms() { ArrayList<Realm> rss = new ArrayList<Realm>(); Set<String> keys = new HashSet<String>(this.realmNameToRealmSet.keySet()); for (String key : keys) { RealmSet rs = this.realmNameToRealmSet.get(key); rss.addAll(rs.values()); } return rss; } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#matchRealm(org.jdiameter.client.api.IRequest) */ @Override public Realm matchRealm(IRequest request) { try { // once again casting... IMessage req = (IMessage) request; String destinationRealm = req.getAvps().getAvp(Avp.DESTINATION_REALM).getDiameterIdentity(); // we have req, we need match, not dummy longest from right BS match. return this.matchRealm(req, destinationRealm); } catch (Exception e) { logger.error("Unable to read Destination-Realm AVP to match realm to request", e); } return null; } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#matchRealm(org.jdiameter.client.api.IAnswer, java.lang.String) */ @Override public Realm matchRealm(IAnswer message, String destRealm) { return this.matchRealm((IMessage) message, destRealm); } /* * (non-Javadoc) * @see org.jdiameter.client.api.controller.IRealmTable#getRealmForPeer(java.lang.String) */ @Override public String getRealmForPeer(String fqdn) { // Collection<Realm> realms = getRealms(); for (Realm r : realms) { IRealm ir = (IRealm) r; if (ir.hasPeerName(fqdn)) { return ir.getName(); } } return null; } /** * @param appId */ @Override public void addLocalApplicationId(ApplicationId appId) { RealmSet rs = getRealmSet(localRealmName, false); rs.addRealm(new RealmImpl(localRealmName, appId, LocalAction.LOCAL, null, null, true, -1, this.localHost) { @Override public boolean isLocal() { return true; } }); } /** * @param appId */ @Override public void removeLocalApplicationId(ApplicationId appId) { RealmSet rs = getRealmSet(localRealmName, false); Realm realm = rs.getRealm(appId); if (realm.isDynamic()) { rs.removeRealm(appId); } } /** * @param localRealm * @param fqdn */ @Override public void addLocalRealm(String localRealm, String fqdn) { this.localRealmName = localRealm; this.localHost = fqdn; getRealmSet(localRealm, true /* adds realm if not present */); } // -------------------- helper methods -------------------- protected Realm matchRealm(IMessage message, String realm) { if (realmExists(realm)) { ApplicationId singleId = message.getSingleApplicationId(); // check on single app id, than we iterate. Realm r = getRealm(realm, singleId); if (r == null) { List<ApplicationId> appIds = message.getApplicationIdAvps(); for (int index = 0; index < appIds.size(); index++) { r = getRealm(realm, appIds.get(index)); if (r != null) { break; } } } return r; } return null; } protected void addRealm(Realm realm) throws InternalException { RealmSet rs = getRealmSet(realm.getName(), true); rs.addRealm(realm); allRealmsSet.add(realm.getName()); } protected RealmSet getRealmSet(String pKey, boolean create) { RealmSet rs = realmNameToRealmSet.get(pKey); if (rs == null && create) { rs = new RealmSet(); realmNameToRealmSet.put(pKey, rs); } return rs; } private class RealmSet { // TODO: use two lists and iterate over index? protected Map<ApplicationId, Realm> appIdToRealm = new HashMap<ApplicationId, Realm>(); /** * @param realm * @throws InternalException */ public void addRealm(Realm realm) { if (this.appIdToRealm.containsKey(realm.getApplicationId())) { Realm presentRealm = this.appIdToRealm.get(realm.getApplicationId()); if (realm.getName().equals(localRealmName)) { // we need to merge - its a local realm, possibly definition // of hosts that can handle requests for us. RealmImpl realmImpl = (RealmImpl) presentRealm; realmImpl.dynamic = false; // ensure its static for (String peerName : ((RealmImpl) realm).getPeerNames()) { realmImpl.addPeerName(peerName); } } else if (!presentRealm.isDynamic() && realm.isDynamic()) { // its a dynamic realm, present is static, we dont have to // do a thing? } else if (presentRealm.isDynamic() && !realm.isDynamic()) { // we need to merge? RealmImpl realmImpl = (RealmImpl) presentRealm; realmImpl.dynamic = false; // make it static :) for (String peerName : ((RealmImpl) realm).getPeerNames()) { realmImpl.addPeerName(peerName); } } else { if (logger.isDebugEnabled()) { logger.debug("Entry for realm '{}', already exists: {}", realm, this); } } } else { this.appIdToRealm.put(realm.getApplicationId(), realm); } } /** * @return */ public Collection<Realm> values() { return this.appIdToRealm.values(); } /** * @return */ public int size() { return this.appIdToRealm.size(); } /** * @param appId * @return */ public Realm getRealm(ApplicationId appId) { return this.appIdToRealm.get(appId); } /** * @param appId * @return */ public Realm removeRealm(ApplicationId appId) { return this.appIdToRealm.remove(appId); } } /* * (non-Javadoc) * @see org.jdiameter.api.RealmTable#getStatistic(java.lang.String) */ @Override public Statistic getStatistic(String realmName) { // FIXME: ... return null; } /* * (non-Javadoc) * @see org.jdiameter.api.Wrapper#isWrapperFor(java.lang.Class) */ @Override public boolean isWrapperFor(Class<?> iface) throws InternalException { return false; } /* * (non-Javadoc) * @see org.jdiameter.api.Wrapper#unwrap(java.lang.Class) */ @Override public <T> T unwrap(Class<T> iface) throws InternalException { return null; } @Override public List<String> getAllRealmSet() { return allRealmsSet; } }