Search in sources :

Example 76 with GroupBucket

use of org.onosproject.net.group.GroupBucket in project onos by opennetworkinglab.

the class OfdpaPipelineTraceable method getGroupsFromInstructions.

// Gets group information from instructions.
private void getGroupsFromInstructions(Map<GroupId, Group> groups, List<Instruction> instructions, TrafficSelector.Builder egressPacket, List<PortNumber> outputPorts, PipelineTraceableHitChain currentHitChain, PipelineTraceableOutput.Builder outputBuilder, PipelineTraceableInput input, boolean dropped) {
    List<Instruction> groupInstructionlist = new ArrayList<>();
    // sort instructions according to priority (larger Instruction.Type ENUM constant first)
    // which enables to treat other actions before the OUTPUT action
    // TODO improve the priority scheme according to the OpenFlow ActionSet spec
    List<Instruction> instructionsSorted = new ArrayList<>();
    instructionsSorted.addAll(instructions);
    instructionsSorted.sort((instr1, instr2) -> Integer.compare(instr2.type().ordinal(), instr1.type().ordinal()));
    // Handles first all non-group instructions
    for (Instruction instruction : instructionsSorted) {
        log.debug("Considering Instruction {}", instruction);
        // to the possible outputs for this packet
        if (!instruction.type().equals(Instruction.Type.GROUP)) {
            // or add the output to the possible outputs for this packet
            if (instruction.type().equals(Instruction.Type.OUTPUT)) {
                buildOutputFromDevice(egressPacket, outputPorts, (OutputInstruction) instruction, currentHitChain, outputBuilder, input.ingressPacket().packet(), dropped);
            } else {
                egressPacket = translateInstruction(egressPacket, instruction);
            }
        } else {
            // Store for later if the instruction is pointing to a group
            groupInstructionlist.add(instruction);
        }
    }
    // handle all the internal instructions pointing to a group.
    for (Instruction instr : groupInstructionlist) {
        Instructions.GroupInstruction groupInstruction = (Instructions.GroupInstruction) instr;
        Group group = groups.get(groupInstruction.groupId());
        // group does not exist in the dataplane
        if (group == null) {
            currentHitChain.setEgressPacket(new PipelineTraceablePacket(egressPacket.build()));
            currentHitChain.dropped();
            outputBuilder.appendToLog("Null group for Instruction " + instr).noGroups().addHitChain(currentHitChain);
            break;
        }
        log.debug("Analyzing group {}", group.id());
        // group is there but there are no members/buckets
        if (group.buckets().buckets().size() == 0) {
            // add the group to the traversed groups
            currentHitChain.addDataPlaneEntity(new DataPlaneEntity(group));
            currentHitChain.setEgressPacket(new PipelineTraceablePacket(egressPacket.build()));
            currentHitChain.dropped();
            outputBuilder.appendToLog("Group " + group.id() + " has no buckets").noMembers().addHitChain(currentHitChain);
            break;
        }
        PipelineTraceableHitChain newHitChain;
        TrafficSelector.Builder newEgressPacket;
        // Cycle in each of the group's buckets and add them to the groups for this Device.
        for (GroupBucket bucket : group.buckets().buckets()) {
            // add the group to the traversed groups
            currentHitChain.addDataPlaneEntity(new DataPlaneEntity(group));
            // Go to the next step - using a copy of the egress packet and of the hit chain
            newHitChain = PipelineTraceableHitChain.emptyHitChain();
            currentHitChain.hitChain().forEach(newHitChain::addDataPlaneEntity);
            newEgressPacket = DefaultTrafficSelector.builder(egressPacket.build());
            getGroupsFromInstructions(groups, bucket.treatment().allInstructions(), newEgressPacket, outputPorts, newHitChain, outputBuilder, input, dropped | isDropped(group.id(), bucket, input.ingressPort()));
        }
    }
}
Also used : Group(org.onosproject.net.group.Group) ArrayList(java.util.ArrayList) Instructions(org.onosproject.net.flow.instructions.Instructions) L2ModificationInstruction(org.onosproject.net.flow.instructions.L2ModificationInstruction) OutputInstruction(org.onosproject.net.flow.instructions.Instructions.OutputInstruction) ModVlanIdInstruction(org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction) Instruction(org.onosproject.net.flow.instructions.Instruction) DataPlaneEntity(org.onosproject.net.DataPlaneEntity) TrafficSelector(org.onosproject.net.flow.TrafficSelector) DefaultTrafficSelector(org.onosproject.net.flow.DefaultTrafficSelector) GroupBucket(org.onosproject.net.group.GroupBucket) PipelineTraceableHitChain(org.onosproject.net.PipelineTraceableHitChain) PipelineTraceablePacket(org.onosproject.net.PipelineTraceablePacket)

Example 77 with GroupBucket

use of org.onosproject.net.group.GroupBucket in project onos by opennetworkinglab.

the class SimpleVirtualGroupStore method addOrUpdateGroupEntry.

@Override
public void addOrUpdateGroupEntry(NetworkId networkId, Group group) {
    // check if this new entry is an update to an existing entry
    StoredGroupEntry existing = null;
    if (groupEntriesById.get(networkId) != null && groupEntriesById.get(networkId).get(group.deviceId()) != null) {
        existing = groupEntriesById.get(networkId).get(group.deviceId()).get(group.id());
    }
    GroupEvent event = null;
    if (existing != null) {
        synchronized (existing) {
            for (GroupBucket bucket : group.buckets().buckets()) {
                Optional<GroupBucket> matchingBucket = existing.buckets().buckets().stream().filter((existingBucket) -> (existingBucket.equals(bucket))).findFirst();
                if (matchingBucket.isPresent()) {
                    ((StoredGroupBucketEntry) matchingBucket.get()).setPackets(bucket.packets());
                    ((StoredGroupBucketEntry) matchingBucket.get()).setBytes(bucket.bytes());
                } else {
                    log.warn("addOrUpdateGroupEntry: No matching " + "buckets to update stats");
                }
            }
            existing.setLife(group.life());
            existing.setPackets(group.packets());
            existing.setBytes(group.bytes());
            if (existing.state() == Group.GroupState.PENDING_ADD) {
                existing.setState(Group.GroupState.ADDED);
                event = new GroupEvent(GroupEvent.Type.GROUP_ADDED, existing);
            } else {
                if (existing.state() == Group.GroupState.PENDING_UPDATE) {
                    existing.setState(Group.GroupState.ADDED);
                }
                event = new GroupEvent(GroupEvent.Type.GROUP_UPDATED, existing);
            }
        }
    }
    if (event != null) {
        notifyDelegate(networkId, event);
    }
}
Also used : HashMap(java.util.HashMap) DefaultGroup(org.onosproject.net.group.DefaultGroup) GroupBucket(org.onosproject.net.group.GroupBucket) GroupOperation(org.onosproject.net.group.GroupOperation) GroupKey(org.onosproject.net.group.GroupKey) Group(org.onosproject.net.group.Group) ArrayList(java.util.ArrayList) ConcurrentMap(java.util.concurrent.ConcurrentMap) Component(org.osgi.service.component.annotations.Component) FluentIterable(com.google.common.collect.FluentIterable) StoredGroupEntry(org.onosproject.net.group.StoredGroupEntry) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) NetworkId(org.onosproject.incubator.net.virtual.NetworkId) Activate(org.osgi.service.component.annotations.Activate) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) StoredGroupBucketEntry(org.onosproject.net.group.StoredGroupBucketEntry) Deactivate(org.osgi.service.component.annotations.Deactivate) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) VirtualNetworkGroupStore(org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore) GroupEvent(org.onosproject.net.group.GroupEvent) Sets(com.google.common.collect.Sets) List(java.util.List) GroupStoreDelegate(org.onosproject.net.group.GroupStoreDelegate) GroupId(org.onosproject.core.GroupId) GroupBuckets(org.onosproject.net.group.GroupBuckets) LoggerFactory.getLogger(org.slf4j.LoggerFactory.getLogger) Optional(java.util.Optional) DeviceId(org.onosproject.net.DeviceId) DefaultGroupDescription(org.onosproject.net.group.DefaultGroupDescription) GroupDescription(org.onosproject.net.group.GroupDescription) GroupBucket(org.onosproject.net.group.GroupBucket) StoredGroupBucketEntry(org.onosproject.net.group.StoredGroupBucketEntry) StoredGroupEntry(org.onosproject.net.group.StoredGroupEntry) GroupEvent(org.onosproject.net.group.GroupEvent)

Example 78 with GroupBucket

use of org.onosproject.net.group.GroupBucket in project onos by opennetworkinglab.

the class SimpleVirtualGroupStore method getUpdatedBucketList.

private List<GroupBucket> getUpdatedBucketList(Group oldGroup, UpdateType type, GroupBuckets buckets) {
    if (type == UpdateType.SET) {
        return buckets.buckets();
    }
    List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
    List<GroupBucket> updatedBucketList = new ArrayList<>();
    boolean groupDescUpdated = false;
    if (type == UpdateType.ADD) {
        List<GroupBucket> newBuckets = buckets.buckets();
        // Add old buckets that will not be updated and check if any will be updated.
        for (GroupBucket oldBucket : oldBuckets) {
            int newBucketIndex = newBuckets.indexOf(oldBucket);
            if (newBucketIndex != -1) {
                GroupBucket newBucket = newBuckets.get(newBucketIndex);
                if (!newBucket.hasSameParameters(oldBucket)) {
                    // Bucket will be updated
                    groupDescUpdated = true;
                }
            } else {
                // Old bucket will remain the same - add it.
                updatedBucketList.add(oldBucket);
            }
        }
        // Add all new buckets
        updatedBucketList.addAll(newBuckets);
        if (!oldBuckets.containsAll(newBuckets)) {
            groupDescUpdated = true;
        }
    } else if (type == UpdateType.REMOVE) {
        List<GroupBucket> bucketsToRemove = buckets.buckets();
        // Check which old buckets should remain
        for (GroupBucket oldBucket : oldBuckets) {
            if (!bucketsToRemove.contains(oldBucket)) {
                updatedBucketList.add(oldBucket);
            } else {
                groupDescUpdated = true;
            }
        }
    }
    if (groupDescUpdated) {
        return updatedBucketList;
    } else {
        return null;
    }
}
Also used : ArrayList(java.util.ArrayList) GroupBucket(org.onosproject.net.group.GroupBucket) ArrayList(java.util.ArrayList) List(java.util.List)

Example 79 with GroupBucket

use of org.onosproject.net.group.GroupBucket in project onos by opennetworkinglab.

the class SimpleVirtualGroupStore method updateGroupDescription.

@Override
public void updateGroupDescription(NetworkId networkId, DeviceId deviceId, GroupKey oldAppCookie, UpdateType type, GroupBuckets newBuckets, GroupKey newAppCookie) {
    // Check if a group is existing with the provided key
    Group oldGroup = getGroup(networkId, deviceId, oldAppCookie);
    if (oldGroup == null) {
        return;
    }
    List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup, type, newBuckets);
    if (newBucketList != null) {
        // Create a new group object from the old group
        GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
        GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
        GroupDescription updatedGroupDesc = new DefaultGroupDescription(oldGroup.deviceId(), oldGroup.type(), updatedBuckets, newCookie, oldGroup.givenGroupId(), oldGroup.appId());
        StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(), updatedGroupDesc);
        newGroup.setState(Group.GroupState.PENDING_UPDATE);
        newGroup.setLife(oldGroup.life());
        newGroup.setPackets(oldGroup.packets());
        newGroup.setBytes(oldGroup.bytes());
        // Remove the old entry from maps and add new entry using new key
        ConcurrentMap<GroupKey, StoredGroupEntry> keyTable = getGroupKeyTable(networkId, oldGroup.deviceId());
        ConcurrentMap<GroupId, StoredGroupEntry> idTable = getGroupIdTable(networkId, oldGroup.deviceId());
        keyTable.remove(oldGroup.appCookie());
        idTable.remove(oldGroup.id());
        keyTable.put(newGroup.appCookie(), newGroup);
        idTable.put(newGroup.id(), newGroup);
        notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_UPDATE_REQUESTED, newGroup));
    }
}
Also used : DefaultGroup(org.onosproject.net.group.DefaultGroup) Group(org.onosproject.net.group.Group) GroupKey(org.onosproject.net.group.GroupKey) DefaultGroup(org.onosproject.net.group.DefaultGroup) GroupBuckets(org.onosproject.net.group.GroupBuckets) StoredGroupEntry(org.onosproject.net.group.StoredGroupEntry) GroupEvent(org.onosproject.net.group.GroupEvent) GroupId(org.onosproject.core.GroupId) DefaultGroupDescription(org.onosproject.net.group.DefaultGroupDescription) GroupDescription(org.onosproject.net.group.GroupDescription) GroupBucket(org.onosproject.net.group.GroupBucket) DefaultGroupDescription(org.onosproject.net.group.DefaultGroupDescription)

Example 80 with GroupBucket

use of org.onosproject.net.group.GroupBucket in project onos by opennetworkinglab.

the class Ofdpa2GroupHandler method createL2L3ChainInternal.

/**
 * Internal implementation of createL2L3Chain.
 * <p>
 * The is_present bit in set_vlan_vid action is required to be 0 in OFDPA i12.
 * Since it is non-OF spec, we need an extension treatment for that.
 * The useSetVlanExtension must be set to false for OFDPA i12.
 * </p>
 *
 * @param treatment that needs to be broken up to create the group chain
 * @param nextId of the next objective that needs this group chain
 * @param appId of the application that sent this next objective
 * @param mpls determines if L3Unicast or MPLSInterface group is created
 * @param meta metadata passed in by the application as part of the nextObjective
 * @param useSetVlanExtension use the setVlanVid extension that has is_present bit set to 0.
 * @return GroupInfo containing the GroupDescription of the
 *         L2Interface group(inner) and the GroupDescription of the (outer)
 *         L3Unicast/MPLSInterface group. May return null if there is an
 *         error in processing the chain
 */
protected GroupInfo createL2L3ChainInternal(TrafficTreatment treatment, int nextId, ApplicationId appId, boolean mpls, TrafficSelector meta, boolean useSetVlanExtension) {
    // for the l2interface group, get vlan and port info
    // for the outer group, get the src/dst mac, and vlan info
    TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
    TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
    VlanId vlanid = null;
    long portNum = 0;
    boolean setVlan = false, popVlan = false;
    MacAddress srcMac;
    MacAddress dstMac;
    for (Instruction ins : treatment.allInstructions()) {
        if (ins.type() == Instruction.Type.L2MODIFICATION) {
            L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
            switch(l2ins.subtype()) {
                case ETH_DST:
                    dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
                    outerTtb.setEthDst(dstMac);
                    break;
                case ETH_SRC:
                    srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
                    outerTtb.setEthSrc(srcMac);
                    break;
                case VLAN_ID:
                    vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
                    if (useSetVlanExtension) {
                        OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
                        outerTtb.extension(ofdpaSetVlanVid, deviceId);
                    } else {
                        outerTtb.setVlanId(vlanid);
                    }
                    setVlan = true;
                    break;
                case VLAN_POP:
                    innerTtb.popVlan();
                    popVlan = true;
                    break;
                case DEC_MPLS_TTL:
                case MPLS_LABEL:
                case MPLS_POP:
                case MPLS_PUSH:
                case VLAN_PCP:
                case VLAN_PUSH:
                default:
                    break;
            }
        } else if (ins.type() == Instruction.Type.OUTPUT) {
            portNum = ((Instructions.OutputInstruction) ins).port().toLong();
            innerTtb.add(ins);
        } else {
            log.debug("Driver does not handle this type of TrafficTreatment" + " instruction in l2l3chain:  {} - {}", ins.type(), ins);
        }
    }
    if (vlanid == null && meta != null) {
        // use metadata if available
        Criterion vidCriterion = meta.getCriterion(VLAN_VID);
        if (vidCriterion != null) {
            vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
        }
        // if vlan is not set, use the vlan in metadata for outerTtb
        if (vlanid != null && !setVlan) {
            if (useSetVlanExtension) {
                OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
                outerTtb.extension(ofdpaSetVlanVid, deviceId);
            } else {
                outerTtb.setVlanId(vlanid);
            }
        }
    }
    if (vlanid == null) {
        log.error("Driver cannot process an L2/L3 group chain without " + "egress vlan information for dev: {} port:{}", deviceId, portNum);
        return null;
    }
    if (!setVlan && !popVlan) {
        // untagged outgoing port
        TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
        temp.popVlan();
        innerTtb.build().allInstructions().forEach(temp::add);
        innerTtb = temp;
    }
    // assemble information for ofdpa l2interface group
    int l2groupId = l2GroupId(vlanid, portNum);
    // a globally unique groupkey that is different for ports in the same device,
    // but different for the same portnumber on different devices. Also different
    // for the various group-types created out of the same next objective.
    int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
    final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
    // assemble information for outer group
    GroupDescription outerGrpDesc;
    if (mpls) {
        // outer group is MPLS Interface
        int mplsInterfaceIndex = getNextAvailableIndex();
        int mplsGroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
        final GroupKey mplsGroupKey = new DefaultGroupKey(appKryo.serialize(mplsInterfaceIndex));
        outerTtb.group(new GroupId(l2groupId));
        // create the mpls-interface group description to wait for the
        // l2 interface group to be processed
        GroupBucket mplsinterfaceGroupBucket = DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
        outerGrpDesc = new DefaultGroupDescription(deviceId, GroupDescription.Type.INDIRECT, new GroupBuckets(Collections.singletonList(mplsinterfaceGroupBucket)), mplsGroupKey, mplsGroupId, appId);
        log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}", deviceId, Integer.toHexString(mplsGroupId), mplsGroupKey, nextId);
    } else {
        // outer group is L3Unicast
        int l3unicastIndex = getNextAvailableIndex();
        int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
        final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3unicastIndex));
        outerTtb.group(new GroupId(l2groupId));
        // create the l3unicast group description to wait for the
        // l2 interface group to be processed
        GroupBucket l3unicastGroupBucket = DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
        outerGrpDesc = new DefaultGroupDescription(deviceId, GroupDescription.Type.INDIRECT, new GroupBuckets(Collections.singletonList(l3unicastGroupBucket)), l3groupkey, l3groupId, appId);
        log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}", deviceId, Integer.toHexString(l3groupId), l3groupkey, nextId);
    }
    // store l2groupkey with the groupChainElem for the outer-group that depends on it
    GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false, deviceId);
    updatePendingGroups(l2groupkey, gce);
    // create group description for the inner l2 interface group
    GroupBucket l2InterfaceGroupBucket = DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
    GroupDescription l2groupDescription = new DefaultGroupDescription(deviceId, GroupDescription.Type.INDIRECT, new GroupBuckets(Collections.singletonList(l2InterfaceGroupBucket)), l2groupkey, l2groupId, appId);
    log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}", deviceId, Integer.toHexString(l2groupId), l2groupkey, nextId);
    return new GroupInfo(l2groupDescription, outerGrpDesc);
}
Also used : OfdpaSetVlanVid(org.onosproject.driver.extensions.OfdpaSetVlanVid) GroupKey(org.onosproject.net.group.GroupKey) DefaultGroupKey(org.onosproject.net.group.DefaultGroupKey) OfdpaGroupHandlerUtility.l2MulticastGroupKey(org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.l2MulticastGroupKey) L2ModificationInstruction(org.onosproject.net.flow.instructions.L2ModificationInstruction) Instructions(org.onosproject.net.flow.instructions.Instructions) DefaultTrafficTreatment(org.onosproject.net.flow.DefaultTrafficTreatment) TrafficTreatment(org.onosproject.net.flow.TrafficTreatment) MacAddress(org.onlab.packet.MacAddress) L2ModificationInstruction(org.onosproject.net.flow.instructions.L2ModificationInstruction) Instruction(org.onosproject.net.flow.instructions.Instruction) GroupInstruction(org.onosproject.net.flow.instructions.Instructions.GroupInstruction) GroupBuckets(org.onosproject.net.group.GroupBuckets) GroupId(org.onosproject.core.GroupId) DefaultGroupDescription(org.onosproject.net.group.DefaultGroupDescription) GroupDescription(org.onosproject.net.group.GroupDescription) PortCriterion(org.onosproject.net.flow.criteria.PortCriterion) TunnelIdCriterion(org.onosproject.net.flow.criteria.TunnelIdCriterion) Criterion(org.onosproject.net.flow.criteria.Criterion) VlanIdCriterion(org.onosproject.net.flow.criteria.VlanIdCriterion) DefaultGroupKey(org.onosproject.net.group.DefaultGroupKey) GroupBucket(org.onosproject.net.group.GroupBucket) DefaultGroupBucket(org.onosproject.net.group.DefaultGroupBucket) DefaultGroupDescription(org.onosproject.net.group.DefaultGroupDescription) VlanId(org.onlab.packet.VlanId)

Aggregations

GroupBucket (org.onosproject.net.group.GroupBucket)108 GroupBuckets (org.onosproject.net.group.GroupBuckets)87 DefaultGroupBucket (org.onosproject.net.group.DefaultGroupBucket)75 DefaultTrafficTreatment (org.onosproject.net.flow.DefaultTrafficTreatment)66 TrafficTreatment (org.onosproject.net.flow.TrafficTreatment)66 DefaultGroupDescription (org.onosproject.net.group.DefaultGroupDescription)62 GroupKey (org.onosproject.net.group.GroupKey)62 DefaultGroupKey (org.onosproject.net.group.DefaultGroupKey)56 GroupDescription (org.onosproject.net.group.GroupDescription)55 ArrayList (java.util.ArrayList)42 Group (org.onosproject.net.group.Group)40 GroupId (org.onosproject.core.GroupId)35 PortNumber (org.onosproject.net.PortNumber)32 DefaultGroup (org.onosproject.net.group.DefaultGroup)26 List (java.util.List)21 DeviceId (org.onosproject.net.DeviceId)20 TrafficSelector (org.onosproject.net.flow.TrafficSelector)20 OfdpaGroupHandlerUtility.l2MulticastGroupKey (org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.l2MulticastGroupKey)17 DefaultTrafficSelector (org.onosproject.net.flow.DefaultTrafficSelector)16 ArrayDeque (java.util.ArrayDeque)15