add warn log with cause to AbstractNeutronNorthbound for 442 and 500
[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 import org.opendaylight.yangtools.yang.common.OperationFailedException;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 public abstract class AbstractNeutronNorthbound<T extends INeutronObject<T>, R extends INeutronRequest<T>,
28         I extends INeutronCRUD<T>> {
29
30     private static final Logger LOG = LoggerFactory.getLogger(AbstractNeutronNorthbound.class);
31
32     // T extends INeutronObject<T> as 0th type argument
33     private static final int NEUTRON_ARGUMENT_TYPE_INDEX = 0;
34     // NeutronRequest extends INeutronRequest<T> as 1st type argument
35     private static final int NEUTRON_REQUEST_TYPE_INDEX = 1;
36
37     protected static final int HTTP_OK_BOTTOM = 200;
38     protected static final int HTTP_OK_TOP = 299;
39     private static final int HTTP_MISSING_DEPENDENCY = 442; // see NEUTRON-158 (also in neutron.e2etest.HttpUtils)
40
41     private static final String INTERFACE_NAME_BASE = " CRUD Interface";
42     private static final String UUID_NO_EXIST_BASE = " UUID does not exist.";
43
44     private final I neutronCRUD;
45
46     protected AbstractNeutronNorthbound(I neutronCRUD) {
47         this.neutronCRUD = Objects.requireNonNull(neutronCRUD, "neutronCRUD");
48     }
49
50     protected final String serviceUnavailable() {
51         return getResourceName() + INTERFACE_NAME_BASE + RestMessages.SERVICEUNAVAILABLE.toString();
52     }
53
54     protected final String uuidNoExist() {
55         return getResourceName() + UUID_NO_EXIST_BASE;
56     }
57
58     protected abstract String getResourceName();
59
60     private <K> Class<K> getActualTypeArgument(final int typeIndex) {
61         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
62         @SuppressWarnings("unchecked")
63         Class<K> cls = (Class<K>) parameterizedType.getActualTypeArguments()[typeIndex];
64         return cls;
65     }
66
67     private R newNeutronRequest(T neutronObject) {
68         // return new R(neutronObject)
69
70         // argumentClass = T.class
71         Class<T> argumentClass = getActualTypeArgument(NEUTRON_ARGUMENT_TYPE_INDEX);
72         // cls = NeturonRequest.class
73         Class<R> cls = getActualTypeArgument(NEUTRON_REQUEST_TYPE_INDEX);
74         try {
75             // ctor = R constructor
76             Constructor<R> ctor = cls.getDeclaredConstructor(argumentClass);
77             return ctor.newInstance(neutronObject);
78         } catch (NoSuchMethodException | InstantiationException
79                  | IllegalAccessException | InvocationTargetException e) {
80             // This case shouldn't happen
81             throw new IllegalArgumentException(e);
82         }
83     }
84
85     protected I getNeutronCRUD() {
86         return this.neutronCRUD;
87     }
88
89     protected Response show(String uuid, List<String> returnFields)
90             throws DatastoreOperationFailedWebApplicationException {
91         try {
92             T ans = neutronCRUD.get(uuid);
93             if (ans == null) {
94                 throw new ResourceNotFoundException(uuidNoExist());
95             }
96
97             if (returnFields.size() > 0) {
98                 return Response.status(HttpURLConnection.HTTP_OK)
99                         .entity(newNeutronRequest(ans.extractFields(returnFields))).build();
100             } else {
101                 return Response.status(HttpURLConnection.HTTP_OK).entity(newNeutronRequest(ans)).build();
102             }
103         } catch (OperationFailedException e) {
104             LOG.warn("get failed due to datastore problem; uuid: {}", uuid);
105             throw new DatastoreOperationFailedWebApplicationException(e);
106         }
107     }
108
109     protected Response create(final R input) throws DatastoreOperationFailedWebApplicationException {
110         try {
111             if (input.isSingleton()) {
112                 T singleton = input.getSingleton();
113
114                 singleton.initDefaults();
115                 if (neutronCRUD.add(singleton).equals(DependencyMissing)) {
116                     return Response.status(HTTP_MISSING_DEPENDENCY).entity(input).build();
117                 }
118             } else {
119                 if (input.getBulk() == null) {
120                     throw new BadRequestException("Invalid requests");
121                 }
122                 for (T test : input.getBulk()) {
123                     test.initDefaults();
124                     if (neutronCRUD.add(test).equals(DependencyMissing)) {
125                         LOG.warn("create failed due to input missing dependencies: {}", input);
126                         return Response.status(HTTP_MISSING_DEPENDENCY).entity(input).build();
127                     }
128                 }
129             }
130             return Response.status(HttpURLConnection.HTTP_CREATED).entity(input).build();
131         } catch (OperationFailedException e) {
132             LOG.warn("create failed due to datastore problem (possibly missing required fields); input: {}", input);
133             throw new DatastoreOperationFailedWebApplicationException(e);
134         }
135     }
136
137     protected void updateDelta(String uuid, T delta, T original) {
138     }
139
140     private boolean checkRevisionNumber(T original, T delta) {
141         // If new update is null ignore the original revision number
142         if (delta.getRevisionNumber() == null) {
143             return false;
144         }
145         // If what is stored is null no need for comparison
146         if (original.getRevisionNumber() == null) {
147             return false;
148         }
149         if (original.getRevisionNumber() > delta.getRevisionNumber()) {
150             return true;
151         }
152         return false;
153     }
154
155     protected Response update(String uuid, final R input) throws DatastoreOperationFailedWebApplicationException {
156         if (!input.isSingleton()) {
157             throw new BadRequestException("Only singleton edit supported");
158         }
159         T delta = input.getSingleton();
160         try {
161             T original = neutronCRUD.get(uuid);
162             if (original == null) {
163                 throw new ResourceNotFoundException(uuidNoExist());
164             }
165             if (checkRevisionNumber(original, delta)) {
166                 return Response.status(HttpURLConnection.HTTP_OK).build();
167             }
168             updateDelta(uuid, delta, original);
169             /*
170              * update the object and return it
171              */
172             Result updateResult = neutronCRUD.update(uuid, delta);
173             if (updateResult.equals(DoesNotExist)) {
174                 throw new ResourceNotFoundException(uuidNoExist());
175             } else if (updateResult.equals(DependencyMissing)) {
176                 LOG.warn("update failed due to missing dependencies; input: {}", input);
177                 return Response.status(HTTP_MISSING_DEPENDENCY).entity(input).build();
178             }
179             T updated = neutronCRUD.get(uuid);
180             return Response.status(HttpURLConnection.HTTP_OK).entity(newNeutronRequest(updated)).build();
181         } catch (OperationFailedException e) {
182             LOG.warn("update failed due to datastore problem (possibly missing required fields); input: {}", input);
183             throw new DatastoreOperationFailedWebApplicationException(e);
184         }
185     }
186
187     protected Response delete(String uuid) throws DatastoreOperationFailedWebApplicationException {
188         try {
189             // remove it and return 204 status
190             if (!neutronCRUD.remove(uuid)) {
191                 throw new ResourceNotFoundException(uuidNoExist());
192             } else {
193                 return Response.status(HttpURLConnection.HTTP_NO_CONTENT).build();
194             }
195         } catch (OperationFailedException e) {
196             LOG.warn("delete failed due to datastore problem; uuid: {}", uuid);
197             throw new DatastoreOperationFailedWebApplicationException(e);
198         }
199     }
200 }