package org.yamcs.parameter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.InvalidIdentification; import org.yamcs.NoPermissionException; import org.yamcs.parameter.ParameterValue; import org.yamcs.protobuf.Yamcs.NamedObjectId; import org.yamcs.security.AuthenticationToken; import org.yamcs.security.Privilege; import org.yamcs.utils.StringConverter; import org.yamcs.xtce.Parameter; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; /** * This sits in front of the ParameterRequestManager and implements subscriptions based on NamedObjectId * taking care to send to the consumers the parameters with the requested id. * * A client can request in fact the same parameter with two different names and they will get it twice each time * * TODO: check privileges and subscription limits * * @author nm * */ public class ParameterWithIdRequestHelper implements ParameterConsumer { ParameterRequestManagerImpl prm; final ParameterWithIdConsumer listener; Logger log=LoggerFactory.getLogger(this.getClass().getName()); Map<Integer, ListMultimap<Parameter, NamedObjectId>> subscriptions = new ConcurrentHashMap<>(); public ParameterWithIdRequestHelper(ParameterRequestManagerImpl prm, ParameterWithIdConsumer listener) { this.prm = prm; this.listener = listener; } public int addRequest(List<NamedObjectId> idList, AuthenticationToken authToken) throws InvalidIdentification, NoPermissionException { ListMultimap<Parameter, NamedObjectId> lm = ArrayListMultimap.create(); List<Parameter> plist = checkNames(idList); for(int i =0; i<idList.size() ; i++) { Parameter p = plist.get(i); checkParameterPrivilege(authToken, p.getQualifiedName()); NamedObjectId id = idList.get(i); lm.put(p, id); } int subscriptionId = prm.addRequest(plist, this); subscriptions.put(subscriptionId, lm); return subscriptionId; } public void addItemsToRequest(int subscriptionId, List<NamedObjectId> idList, AuthenticationToken authToken) throws InvalidIdentification, NoPermissionException { ListMultimap<Parameter, NamedObjectId> subscr = subscriptions.get(subscriptionId); if(subscr==null) { log.warn("add item requested for an invalid subscription id {}", subscriptionId); return; } List<Parameter> plist = checkNames(idList); synchronized(subscr) { for(int i =0; i<idList.size() ; i++) { Parameter p = plist.get(i); checkParameterPrivilege(authToken, p.getQualifiedName()); NamedObjectId id = idList.get(i); if(subscr.containsEntry(p, id)) { log.info("Ignoring duplicate subscription for '{}', id: {}", p.getName(), StringConverter.idToString(id)); continue; } subscr.put(p, id); } } prm.addItemsToRequest(subscriptionId, plist); } private List<Parameter> checkNames(List<NamedObjectId> plist) throws InvalidIdentification { List<NamedObjectId> invalid = new ArrayList<NamedObjectId>(); List<Parameter> result = new ArrayList<Parameter>(); for(NamedObjectId id:plist) { try { Parameter p = prm.getParameter(id); result.add(p); } catch (InvalidIdentification e) { invalid.add(id); } } if(!invalid.isEmpty()) { throw new InvalidIdentification(invalid); } return result; } public void removeRequest(int subscriptionId) { if(!subscriptions.containsKey(subscriptionId)) { log.warn("remove requested for an invalid subscription id {}", subscriptionId); return; } prm.removeRequest(subscriptionId); } public void removeItemsFromRequest(int subscriptionId, List<NamedObjectId> parameterIds, AuthenticationToken authToken) throws NoPermissionException { ListMultimap<Parameter, NamedObjectId> subscr = subscriptions.get(subscriptionId); if(subscr==null) { log.warn("remove requested for an invalid subscription id {}", subscriptionId); return; } List<Parameter> paramsToRemove = new ArrayList<Parameter>(); synchronized(subscr) { Iterator<Entry<Parameter, NamedObjectId>> it = subscr.entries().iterator(); while(it.hasNext()) { Entry<Parameter, NamedObjectId> e = it.next(); checkParameterPrivilege(authToken, e.getKey().getQualifiedName()); if(parameterIds.contains(e.getValue())) { paramsToRemove.add(e.getKey()); } } } if(!paramsToRemove.isEmpty()) { prm.removeItemsFromRequest(subscriptionId, paramsToRemove); } } public ParameterRequestManagerImpl getPrm() { return prm; } public int subscribeAll(String namespace, AuthenticationToken authToken) throws NoPermissionException { checkParameterPrivilege(authToken, ".*"); return prm.subscribeAll(this); } @Override public void updateItems(int subscriptionId, List<ParameterValue> items) { ListMultimap<Parameter, NamedObjectId> subscription = subscriptions.get(subscriptionId); if(subscription==null) { //probably the subscription has just been removed log.debug("Received an updateItems for an unknown subscription {}", subscriptionId); return; } List<ParameterValueWithId> plist = new ArrayList<ParameterValueWithId>(items.size()); synchronized(subscription) { for(ParameterValue pv: items) { List<NamedObjectId> idList = subscription.get(pv.getParameter()); if(idList==null || idList.isEmpty()) { log.warn("Received values for a parameter not subscribed: {}", pv.getParameter()); continue; } for(NamedObjectId id:idList) { ParameterValueWithId pvwi = new ParameterValueWithId(pv, id); plist.add(pvwi); } } } listener.update(subscriptionId, plist); } public List<ParameterValueWithId> getValuesFromCache(List<NamedObjectId> idList, AuthenticationToken authToken) throws InvalidIdentification, NoPermissionException { List<Parameter> params = checkNames(idList); ListMultimap<Parameter, NamedObjectId> lm = ArrayListMultimap.create(); for(int i =0; i<idList.size() ; i++) { Parameter p = params.get(i); checkParameterPrivilege(authToken, p.getQualifiedName()); NamedObjectId id = idList.get(i); lm.put(p, id); } List<ParameterValue> values = prm.getValuesFromCache(params); List<ParameterValueWithId> plist = new ArrayList<ParameterValueWithId>(values.size()); for(ParameterValue pv: values) { List<NamedObjectId> l = lm.get(pv.getParameter()); if(l==null) { log.warn("Received values for a parameter not requested: {}", pv.getParameter()); continue; } for(NamedObjectId id:l) { ParameterValueWithId pvwi = new ParameterValueWithId(pv, id); plist.add(pvwi); } } return plist; } public void switchPrm(ParameterRequestManagerImpl newPrm, AuthenticationToken authToken) throws InvalidIdentification, NoPermissionException { for(int subscriptionId: subscriptions.keySet()) { List<Parameter> plist = prm.removeRequest(subscriptionId); // checking permission for(Parameter p : plist) checkParameterPrivilege(authToken, p.getQualifiedName()); newPrm.addRequest(subscriptionId, plist, this); } prm=newPrm; } public boolean hasParameterCache() { return prm.hasParameterCache(); } /** * Check if the user has a privilege for the specified parameter name * @param authToken * @param parameterName * @throws NoPermissionException */ private void checkParameterPrivilege(AuthenticationToken authToken, String parameterName) throws NoPermissionException { if(!Privilege.getInstance().hasPrivilege1(authToken, Privilege.Type.TM_PARAMETER, parameterName)) { throw new NoPermissionException("User " + authToken + " has no permission for parameter " + parameterName); } } }