Remove utility class constructor exceptions
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / util / FlatBatchUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.impl.util;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Function;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.EnumSet;
19 import java.util.List;
20 import java.util.Map;
21 import org.opendaylight.openflowplugin.impl.services.batch.BatchPlanStep;
22 import org.opendaylight.openflowplugin.impl.services.batch.BatchStepType;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutput;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutputBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.flow.crud._case.aug.FlatBatchAddFlowCase;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.flow.crud._case.aug.FlatBatchRemoveFlowCase;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.flow.crud._case.aug.FlatBatchUpdateFlowCase;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.group.crud._case.aug.FlatBatchAddGroupCase;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.group.crud._case.aug.FlatBatchRemoveGroupCase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.group.crud._case.aug.FlatBatchUpdateGroupCase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.meter.crud._case.aug.FlatBatchAddMeterCase;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.meter.crud._case.aug.FlatBatchRemoveMeterCase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.flat.batch.meter.crud._case.aug.FlatBatchUpdateMeterCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.Batch;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.BatchChoice;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.output.BatchFailure;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.output.BatchFailureKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.service.batch.common.rev160322.BatchOrderGrouping;
39 import org.opendaylight.yangtools.yang.binding.DataContainer;
40 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
41 import org.opendaylight.yangtools.yang.common.RpcError;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
44
45 /**
46  * Provides flat batch util methods.
47  */
48 public final class FlatBatchUtil {
49     private FlatBatchUtil() {
50         // Hidden on purpose
51     }
52
53     public static void markBarriersWhereNeeded(final List<BatchPlanStep> batchPlan) {
54         final EnumSet<BatchStepType> previousTypes = EnumSet.noneOf(BatchStepType.class);
55
56         BatchPlanStep previousPlanStep = null;
57         for (BatchPlanStep planStep : batchPlan) {
58             final BatchStepType type = planStep.getStepType();
59             if (!previousTypes.isEmpty() && decideBarrier(previousTypes, type)) {
60                 previousPlanStep.setBarrierAfter(true);
61                 previousTypes.clear();
62             }
63             previousTypes.add(type);
64             previousPlanStep = planStep;
65         }
66     }
67
68     @VisibleForTesting
69     static boolean decideBarrier(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
70         return isFlowBarrierNeeded(previousTypes, type)
71                 || isGroupBarrierNeeded(previousTypes, type)
72                 || isMeterBarrierNeeded(previousTypes, type);
73     }
74
75     private static boolean isFlowBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
76         return (type == BatchStepType.FLOW_ADD
77                 || type == BatchStepType.FLOW_UPDATE)
78                 && (previousTypes.contains(BatchStepType.GROUP_ADD)
79                 || previousTypes.contains(BatchStepType.METER_ADD));
80     }
81
82     private static boolean isGroupBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
83         return type == BatchStepType.GROUP_ADD
84                 && (previousTypes.contains(BatchStepType.GROUP_ADD)
85                 || previousTypes.contains(BatchStepType.GROUP_UPDATE))
86                 || type == BatchStepType.GROUP_REMOVE
87                 && (previousTypes.contains(BatchStepType.FLOW_REMOVE)
88                 || previousTypes.contains(BatchStepType.FLOW_UPDATE)
89                 || previousTypes.contains(BatchStepType.GROUP_REMOVE)
90                 || previousTypes.contains(BatchStepType.GROUP_UPDATE));
91     }
92
93     private static boolean isMeterBarrierNeeded(final EnumSet<BatchStepType> previousTypes, final BatchStepType type) {
94         return type == BatchStepType.METER_REMOVE
95                 && (previousTypes.contains(BatchStepType.FLOW_REMOVE)
96                 || previousTypes.contains(BatchStepType.FLOW_UPDATE));
97     }
98
99     public static List<BatchPlanStep> assembleBatchPlan(final Collection<Batch> batches) {
100         final List<BatchPlanStep> plan = new ArrayList<>();
101
102         BatchPlanStep planStep;
103         for (Batch batch : batches) {
104             final BatchStepType nextStepType = detectBatchStepType(batch.getBatchChoice());
105
106             planStep = new BatchPlanStep(nextStepType);
107             planStep.getTaskBag().addAll(extractBatchData(planStep.getStepType(), batch.getBatchChoice()));
108             if (!planStep.isEmpty()) {
109                 plan.add(planStep);
110             }
111         }
112
113         return plan;
114     }
115
116     private static Collection<? extends BatchOrderGrouping> extractBatchData(final BatchStepType batchStepType,
117                                                                        final BatchChoice batchChoice) {
118         final Collection<? extends BatchOrderGrouping> batchData;
119         switch (batchStepType) {
120             case FLOW_ADD:
121                 batchData = ((FlatBatchAddFlowCase) batchChoice).nonnullFlatBatchAddFlow().values();
122                 break;
123             case FLOW_REMOVE:
124                 batchData = ((FlatBatchRemoveFlowCase) batchChoice).nonnullFlatBatchRemoveFlow().values();
125                 break;
126             case FLOW_UPDATE:
127                 batchData = ((FlatBatchUpdateFlowCase) batchChoice).nonnullFlatBatchUpdateFlow().values();
128                 break;
129             case GROUP_ADD:
130                 batchData = ((FlatBatchAddGroupCase) batchChoice).nonnullFlatBatchAddGroup().values();
131                 break;
132             case GROUP_REMOVE:
133                 batchData = ((FlatBatchRemoveGroupCase) batchChoice).nonnullFlatBatchRemoveGroup().values();
134                 break;
135             case GROUP_UPDATE:
136                 batchData = ((FlatBatchUpdateGroupCase) batchChoice).nonnullFlatBatchUpdateGroup().values();
137                 break;
138             case METER_ADD:
139                 batchData = ((FlatBatchAddMeterCase) batchChoice).nonnullFlatBatchAddMeter().values();
140                 break;
141             case METER_REMOVE:
142                 batchData = ((FlatBatchRemoveMeterCase) batchChoice).nonnullFlatBatchRemoveMeter().values();
143                 break;
144             case METER_UPDATE:
145                 batchData = ((FlatBatchUpdateMeterCase) batchChoice).nonnullFlatBatchUpdateMeter().values();
146                 break;
147             default:
148                 throw new IllegalArgumentException("Unsupported batch step type obtained: " + batchStepType);
149         }
150         return batchData;
151     }
152
153     @VisibleForTesting
154     static <T extends BatchChoice> BatchStepType detectBatchStepType(final T batchCase) {
155         final BatchStepType type;
156         final Class<? extends DataContainer> implementedInterface = batchCase.implementedInterface();
157
158         // FIXME: use a lookup table instead of this cascade
159         if (FlatBatchAddFlowCase.class.equals(implementedInterface)) {
160             type = BatchStepType.FLOW_ADD;
161         } else if (FlatBatchRemoveFlowCase.class.equals(implementedInterface)) {
162             type = BatchStepType.FLOW_REMOVE;
163         } else if (FlatBatchUpdateFlowCase.class.equals(implementedInterface)) {
164             type = BatchStepType.FLOW_UPDATE;
165         } else if (FlatBatchAddGroupCase.class.equals(implementedInterface)) {
166             type = BatchStepType.GROUP_ADD;
167         } else if (FlatBatchRemoveGroupCase.class.equals(implementedInterface)) {
168             type = BatchStepType.GROUP_REMOVE;
169         } else if (FlatBatchUpdateGroupCase.class.equals(implementedInterface)) {
170             type = BatchStepType.GROUP_UPDATE;
171         } else if (FlatBatchAddMeterCase.class.equals(implementedInterface)) {
172             type = BatchStepType.METER_ADD;
173         } else if (FlatBatchRemoveMeterCase.class.equals(implementedInterface)) {
174             type = BatchStepType.METER_REMOVE;
175         } else if (FlatBatchUpdateMeterCase.class.equals(implementedInterface)) {
176             type = BatchStepType.METER_UPDATE;
177         } else {
178             throw new IllegalArgumentException("Unsupported batch obtained: " + implementedInterface);
179         }
180         return type;
181     }
182
183     @VisibleForTesting
184     static Function<List<RpcResult<ProcessFlatBatchOutput>>, RpcResult<ProcessFlatBatchOutput>> mergeRpcResults() {
185         return jobsResults -> {
186             boolean isSuccessful = true;
187             List<RpcError> rpcErrors = new ArrayList<>();
188             BindingMap.Builder<BatchFailureKey, BatchFailure> batchFailures = BindingMap.orderedBuilder();
189
190             for (RpcResult<ProcessFlatBatchOutput> jobResult : jobsResults) {
191                 if (jobResult != null) {
192                     isSuccessful = isSuccessful && jobResult.isSuccessful();
193                     rpcErrors.addAll(jobResult.getErrors());
194                     batchFailures.addAll(jobResult.getResult().nonnullBatchFailure().values());
195                 }
196             }
197
198             return RpcResultBuilder.<ProcessFlatBatchOutput>status(isSuccessful)
199                     .withRpcErrors(rpcErrors)
200                     .withResult(new ProcessFlatBatchOutputBuilder().setBatchFailure(batchFailures.build()).build())
201                     .build();
202         };
203     }
204
205     /**
206      * Merge list of Futures with partial results into one ListenableFuture with single result.
207      * @param firedJobs list of ListenableFutures with RPC results {@link ProcessFlatBatchOutput}
208      * @return ListenableFuture of RPC result with combined status and all errors + batch failures
209      */
210     public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> mergeJobsResultsFutures(
211             final List<ListenableFuture<RpcResult<ProcessFlatBatchOutput>>> firedJobs) {
212         return Futures.transform(Futures.successfulAsList(firedJobs),
213                 mergeRpcResults(),
214                 MoreExecutors.directExecutor());
215     }
216
217     /**
218      * Creates empty result future for flat batch service.
219      * @param status RPC result status
220      * @return ListenableFuture of RPC result with empty list of errors and batch failures
221      */
222     public static ListenableFuture<RpcResult<ProcessFlatBatchOutput>> createEmptyRpcBatchResultFuture(
223             final boolean status) {
224         return RpcResultBuilder.<ProcessFlatBatchOutput>status(status)
225                                .withRpcErrors(new ArrayList<>())
226                                .withResult(new ProcessFlatBatchOutputBuilder().setBatchFailure(Map.of()).build())
227                                .buildFuture();
228     }
229 }