diff:
Код:
5,9c5,7
< import java.util.Arrays;
< import java.util.HashSet;
< import java.util.List;
< import java.util.Map;
< import java.util.Set;
---
> import java.util.*;
> import java.util.regex.Matcher;
> import java.util.regex.Pattern;
14a13,14
> import ru.bitel.bgbilling.common.BGException;
> import ru.bitel.bgbilling.kernel.container.managed.ServerContext;
28a29,30
> import ru.bitel.bgbilling.modules.inet.api.common.bean.InetOption;
> import ru.bitel.bgbilling.modules.inet.api.server.bean.InetOptionDao;
31a34,35
> import ru.bitel.common.Preferences;
> import ru.bitel.common.TreeUtils;
32a37
> import ru.bitel.common.worker.ThreadContext;
50a56,60
> /**
> * id модуля
> */
> protected int moduleId;
>
157a168
> this.moduleId = moduleId;
185c196,204
< optionRadiusAttributesMap = RadiusAttributeSet.newRadiusAttributeSetRealmMap( deviceConfig, deviceConfig.get( "sa.radius.option.attributesPrefix", defaultOptionsPrefix ), "attributes" );
---
> String optionsPrefix = deviceConfig.get( "sa.radius.option.attributesPrefix", defaultOptionsPrefix );
> String param = "attributes";
> ParameterMap newDeviceConfig = deviceConfig;
> try{
> newDeviceConfig = parseInetOptionRadiusAttributesTemplates(deviceConfig, optionsPrefix, "template", param);
> }catch (BGException e){
> logger.error("Error parsing option radius attribute templates: ", e);
> }
> optionRadiusAttributesMap = RadiusAttributeSet.newRadiusAttributeSetRealmMap( newDeviceConfig, optionsPrefix, param );
359a379,550
>
> /**
> * Функция обрабатывает конфиг устройства, находит в нём вхождения вида <prefix><option_id>.<param>=<template> или
> * <prefix><option_id>.<realm>.<param>=<template> и добавляет соответствующие параметры в результирующий конфиг для всех
> * дочерних опций sub_option_id опции option_id:
> * <prefix><sub_option_id>.<targetParam>=<value> или <prefix><sub_option_id>.<realm>.<targetParam>=<value>
> * значение параметра value получается из шаблона template подстановкой значений параметров из конфига опции.
> * Например cisco-SSG-Service-Info=QU;;$(speed)000;;$(cisco.burst);;D;;$(speed)000;;$(cisco.burst)
> * ->
> * [
> * speed=100
> * cisco.burst=50000
> * ]
> * ->
> * cisco-SSG-Service-Info=QU;;100000;;50000;;D;;100000;;50000
> * При этом конфиги опций наследуются, начиная с родительской option_id
> * Также при обработке дерева к конфигу опции добавляется параметр name=option.getTitle(), т.е. в шаблонах можно
> * использовать параметр $(name) - имя опции
> *
> * @param deviceConfig - исходный конфиг устройства
> * @param prefix - ex "radius.inetOption."
> * @param param - ex "template"
> * @param targetParam - ex "attributes"
> * @return возвращает новый конфиг устройства, в который добавлены атрибуты опций, полученные по шалонам
> * @see #insertVariablesValues(String, ru.bitel.common.ParameterMap)
> * @see #processSubTree
> */
> private ParameterMap parseInetOptionRadiusAttributesTemplates(ParameterMap deviceConfig,
> String prefix, String param, String targetParam) throws BGException {
> Map<String, String> conf = new HashMap<String, String>();
> ServerContext ctx = (ServerContext) ThreadContext.get();
> InetOptionDao inetOptionDao = new InetOptionDao(ctx.getConnectionSet().getSlaveConnection(), this.moduleId);
>
> //Список всех опций
> List<InetOption> optionList = inetOptionDao.list();
> //Карта : id опции -> опция из optionList
> Map<Integer, InetOption> optionMap = new HashMap<Integer, InetOption>();
> for (InetOption d : optionList)
> {
> optionMap.put(d.getId(), d);
> }
> //Строим полное дерево опций
> InetOption root = new InetOption();
> root.setGroupIntersection(true);
> root = TreeUtils.tree(optionList, root);//Теперь всем опциям из optionList назначены потомки
>
> //
> //Можно было бы зайти с другой стороны - перебирать все параметры вида "prefix<id>.param", брать по id опцию и
> //обрабатывать её поддерево, но тогда нарушается порядокобработки - если в дереве одной опции с шаблоном будет
> //другой шаблон, то не факт, что последний перетрётся. В случае с рекурсивным перебором - всё ок
> processTree(root,deviceConfig,conf,prefix,param,targetParam);
> //перебираем все , например radius.inetOption.1.template
>
> inetOptionDao.recycle();
>
> //Поверх полученных значений пишем значения из конфига, т.к. они более приоритетны, чем шаблоны.
> //Т.е. если в deviceConfig задать шаблон radius.inetOption.10.template = xxx и
> //radius.inetOption.12.attributes = yyy, где опция 12 лежит в субветке опции 10,
> //то radius.inetOption.12.attributes будет приоритетнее
> ParameterMap newConf = (new Preferences(conf)).inherit(deviceConfig);
>
> return newConf;
> }
>
> /**
> * Рекурсивная обработка всех опций модуля Inet - если для какой то опции в конфиге устройства deviceConfig найдётся
> * параметр <prefix><option_id>.<param>=... или параметр <prefix><option_id>.<realmname>.<param>=... ,
> * то опять же рекурсивно обрабатываем субдерево для этой опции: см. processSubTree
> *
> * @param node
> * @param deviceConfig
> * @param conf
> * @param prefix
> * @param param
> * @param targetParam
> *
> * @see #processSubTree(ru.bitel.bgbilling.modules.inet.api.common.bean.InetOption, java.util.Map, String, String, String, ru.bitel.common.ParameterMap)
> */
> private void processTree(InetOption node, ParameterMap deviceConfig, Map<String, String> conf, String prefix, String param, String targetParam){
> ParameterMap sub = deviceConfig.sub(prefix+node.getId()+".");
> if(null==sub){return;}
> String templateValue;
> String targetParamLocal;
>
> for(Map.Entry<String, ParameterMap> e : sub.subKeyed("").entrySet()){
> targetParamLocal = targetParam; //"attributes"
> if(param.equals(e.getKey())){//radius.inetOption.1.template=<templateValue>
> templateValue=e.getValue().get("", null);
> }else{//radius.inetOption.1.<realm>.template
> templateValue=e.getValue().get(param, null);
> targetParamLocal=e.getKey()+"."+targetParam; //"<realm>.attributes"
> }
> if(null!=templateValue){
> //рекурсивно обрабатываем поддерево
> this.processSubTree(node, conf, prefix, targetParamLocal, templateValue, null);
> }
> }
>
> //рекурсия
> if(node.getChildren()!=null){
> for (InetOption child : node.getChildren()){
> processTree(child, deviceConfig, conf, prefix, param, targetParam);
> }
> }
> }
>
> /**
> * Рекурсивно обрабатываем поддерево опций модуля Inet, пишем в конфиг conf параметры <prefix><node.id>.<param>=parseTemplate(templateValue)
> * @param node - родительский узел, должен содержать потомков в getChildren()
> * @param conf - итоговый конфиг, куда пишем результаты
> * @param prefix - префикс параметра конфига, например "inetOption."
> * @param param - суффикс параметра, например "attributes"
> * @param templateValue - шаблон значения параметра. обрабатывается функцией insertVariablesValues
> * @param parentNodeConf - значение родительский конфиг. Необходимо для наследования конфигов в пределах поддерева. Может быть null.
> * @see #insertVariablesValues(String, ru.bitel.common.ParameterMap)
> */
> private void processSubTree(InetOption node, Map<String, String> conf, String prefix, String param, String templateValue, ParameterMap parentNodeConf){
> ParameterMap nodeConf = new Preferences(node.getConfig(), "\n");
> //Добавляем в конфиг опции параметр name=<имя_опции>
> ((Preferences)nodeConf).set("name", node.getTitle());
> //Конфиг для опций будет наследоваться внутри субветки
> if(parentNodeConf!=null){
> nodeConf = parentNodeConf.inherit(nodeConf);
> }
>
> //Обрабатываем шаблон, добавляем текущий узел в итоговый конфиг
> //ex: radius.inetOption.<id>.attributes=blablabla;foo;
> conf.put(prefix+node.getId()+"."+param, insertVariablesValues(templateValue, nodeConf));
>
> //рекурсивно обрабатываем дочерние узлы
> if(node.getChildren()!=null){
> for(InetOption child : node.getChildren()){
> processSubTree(child, conf, prefix, param, templateValue, nodeConf);
> }
> }
> }
>
> /**
> * Подстановка переменных из data в value по шаблону "blabla$(varibale.1.value)" -> "blabla123"
> * @param value example: $(speed)000;;$(cisco.burst);;D;;$(speed)000;;$(cisco.burst)
> * @param data соответствие значений, например: speed=100, cisco.burst=50000
> * @return
> */
> private String insertVariablesValues(String value, ParameterMap data){
>
> Matcher m = Pattern.compile("\\$\\(([\\w\\.]+)\\)").matcher(value);
> if (m.find()){
> StringBuffer newValue = new StringBuffer(value.length() + 16);
> String varName = m.group(1);
>
> String var = data.get(varName, null);
> if (var != null){
> var = Matcher.quoteReplacement(var);
> m.appendReplacement(newValue, var);
> }
>
> while (m.find()){
> var = data.get(m.group(1), null);
> if (var == null)
> continue;
> var = Matcher.quoteReplacement(var);
> m.appendReplacement(newValue, var);
> }
>
> m.appendTail(newValue);
>
> return newValue.toString();
> }
>
> return value;
> }
>