39a9b4d340a420ce503f18cefcd6493430eb7f0c
[neutron.git] / northbound-api / src / main / java / org / opendaylight / neutron / northbound / api / AbstractNeutronNorthbound.java
1 /*
2  * Copyright (c) 2018 Intel Corporation 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 package org.opendaylight.neutron.northbound.api;
9
10 import static org.opendaylight.neutron.spi.INeutronCRUD.Result.DependencyMissing;
11 import static org.opendaylight.neutron.spi.INeutronCRUD.Result.DoesNotExist;
12
13 import java.lang.reflect.Constructor;
14 import java.lang.reflect.InvocationTargetException;
15 import java.lang.reflect.ParameterizedType;
16 import java.net.HttpURLConnection;
17 import java.util.List;
18 import java.util.Objects;
19 import javax.ws.rs.core.Response;
20 import org.opendaylight.neutron.spi.INeutronCRUD;
21 import org.opendaylight.neutron.spi.INeutronCRUD.Result;
22 import org.opendaylight.neutron.spi.INeutronObject;
23
24 public abstract class AbstractNeutronNorthbound<T extends INeutronObject<T>, R extends INeutronRequest<T>,
25         I extends INeutronCRUD<T>> {
26
27     // T extends INeutronObject<T> as 0th type argument
28     private static final int NEUTRON_ARGUMENT_TYPE_INDEX = 0;
29     // NeutronRequest extends INeutronRequest<T> as 1st type argument
30     private static final int NEUTRON_REQUEST_TYPE_INDEX = 1;
31
32     protected static final int HTTP_OK_BOTTOM = 200;
33     protected static final int HTTP_OK_TOP = 299;
34     private static final int HTTP_MISSING_DEPENDENCY = 442; // see NEUTRON-158 (also in neutron.e2etest.HttpUtils)
35
36     private static final String INTERFACE_NAME_BASE = " CRUD Interface";
37     private static final String UUID_NO_EXIST_BASE = " UUID does not exist.";
38
39     private final I neutronCRUD;
40
41     protected AbstractNeutronNorthbound(I neutronCRUD) {
42         this.neutronCRUD = Objects.requireNonNull(neutronCRUD, "neutronCRUD");
43     }
44
45     protected final String serviceUnavailable() {
46         return getResourceName() + INTERFACE_NAME_BASE + RestMessages.SERVICEUNAVAILABLE.toString();
47     }
48
49     protected final String uuidNoExist() {
50         return getResourceName() + UUID_NO_EXIST_BASE;
51     }
52
53     protected abstract String getResourceName();
54
55     private <K> Class<K> getActualTypeArgument(final int typeIndex) {
56         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
57         @SuppressWarnings("unchecked")
58         Class<K> cls = (Class<K>) parameterizedType.getActualTypeArguments()[typeIndex];
59         return cls;
60     }
61
62     private R newNeutronRequest(T neutronObject) {
63         // return new R(neutronObject)
64
65         // argumentClass = T.class
66         Class<T> argumentClass = getActualTypeArgument(NEUTRON_ARGUMENT_TYPE_INDEX);
67         // cls = NeturonRequest.class
68         Class<R> cls = getActualTypeArgument(NEUTRON_REQUEST_TYPE_INDEX);
69         try {
70             // ctor = R constructor
71             Constructor<R> ctor = cls.getDeclaredConstructor(argumentClass);
72             return ctor.newInstance(neutronObject);
73         } catch (NoSuchMethodException | InstantiationException
74                  | IllegalAccessException | InvocationTargetException e) {
75             // This case shouldn't happen
76             throw new IllegalArgumentException(e);
77         }
78     }
79
80     protected I getNeutronCRUD() {
81         return this.neutronCRUD;
82     }
83
84     protected Response show(String uuid,
85             // return fields
86             List<String> fields) {
87         T ans = neutronCRUD.get(uuid);
88         if (ans == null) {
89             throw new ResourceNotFoundException(uuidNoExist());
90         }
91
92         if (fields.size() > 0) {
93             return Response.status(HttpURLConnection.HTTP_OK).entity(newNeutronRequest(ans.extractFields(fields)))
94                     .build();
95         } else {
96             return Response.status(HttpURLConnection.HTTP_OK).entity(newNeutronRequest(ans)).build();
97         }
98     }
99
100     protected Response create(final R input) {
101         if (input.isSingleton()) {
102             T singleton = input.getSingleton();
103
104             singleton.initDefaults();
105             if (neutronCRUD.add(singleton).equals(DependencyMissing)) {
106                 return Response.status(HTTP_MISSING_DEPENDENCY).entity(input).build();
107             }
108         } else {
109             if (input.getBulk() == null) {
110                 throw new BadRequestException("Invalid requests");
111             }
112             for (T test : input.getBulk()) {
113                 test.initDefaults();
114                 if (neutronCRUD.add(test).equals(DependencyMissing)) {
115                     return Response.status(HTTP_MISSING_DEPENDENCY).entity(input).build();
116                 }
117             }
118         }
119         return Response.status(HttpURLConnection.HTTP_CREATED).entity(input).build();
120     }
121
122     protected void updateDelta(String uuid, T delta, T original) {
123     }
124
125     private boolean checkRevisionNumber(T original, T delta) {
126         // If new update is null ignore the original revision number
127         if (delta.getRevisionNumber() == null) {
128             return false;
129         }
130         // If what is stored is null no need for comparison
131         if (original.getRevisionNumber() == null) {
132             return false;
133         }
134         if (original.getRevisionNumber() > delta.getRevisionNumber()) {
135             return true;
136         }
137         return false;
138     }
139
140     protected Response update(String uuid, final R input) {
141         if (!input.isSingleton()) {
142             throw new BadRequestException("Only singleton edit supported");
143         }
144         T delta = input.getSingleton();
145         T original = neutronCRUD.get(uuid);
146         if (original == null) {
147             throw new ResourceNotFoundException(uuidNoExist());
148         }
149         if (checkRevisionNumber(original, delta)) {
150             return Response.status(HttpURLConnection.HTTP_OK).build();
151         }
152         updateDelta(uuid, delta, original);
153         /*
154          * update the object and return it
155          */
156         Result updateResult = neutronCRUD.update(uuid, delta);
157         if (updateResult.equals(DoesNotExist)) {
158             throw new ResourceNotFoundException(uuidNoExist());
159         } else if (updateResult.equals(DependencyMissing)) {
160             return Response.status(HTTP_MISSING_DEPENDENCY).entity(input).build();
161         }
162         T updated = neutronCRUD.get(uuid);
163         return Response.status(HttpURLConnection.HTTP_OK).entity(newNeutronRequest(updated)).build();
164     }
165
166     protected Response delete(String uuid) {
167         /*
168          * remove it and return 204 status
169          */
170         if (!neutronCRUD.remove(uuid)) {
171             throw new ResourceNotFoundException(uuidNoExist());
172         }
173
174         return Response.status(HttpURLConnection.HTTP_NO_CONTENT).build();
175     }
176 }