2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2018 Amdocs
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.logging.ref.slf4j.demo.component;
24 import java.util.Locale;
26 import java.util.UUID;
28 import javax.servlet.http.HttpServletRequest;
30 import com.mashape.unirest.http.HttpResponse;
31 import com.mashape.unirest.http.JsonNode;
32 import com.mashape.unirest.http.Unirest;
33 import com.mashape.unirest.http.exceptions.UnirestException;
34 import org.apache.commons.lang3.StringUtils;
35 import org.json.JSONObject;
36 import org.onap.logging.ref.slf4j.common.ONAPLogAdapter;
37 import org.onap.logging.ref.slf4j.common.ONAPLogConstants;
38 import org.onap.logging.ref.slf4j.demo.bean.Request;
39 import org.onap.logging.ref.slf4j.demo.bean.Response;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import org.springframework.http.MediaType;
44 import org.springframework.mock.web.MockHttpServletRequest;
45 import org.springframework.web.bind.annotation.RequestMapping;
46 import org.springframework.web.bind.annotation.RequestMethod;
47 import org.springframework.web.bind.annotation.RestController;
50 * Base class for <tt>Alpha</tt>, <tt>Beta</tt> and <tt>Gamma</tt>
51 * and <tt>Delta</tt> controllers, implementing all the actual logic.
53 * <p>(The subclasses provide nothing but identifiers to allow them
54 * to be distinguished from one another, for the purposes of addressing
55 * requests and generating the call graph from their logger output.)</p>
58 public abstract class AbstractComponent {
61 * Test switch, routing invocations between components in-process,
62 * rather than via REST over HTTP.
64 private static boolean sInProcess;
67 * Get service identifier, used to derive {@link #getServiceName()},
68 * <tt>PartnerName</tt>, etc.
69 * @return <tt>alpha</tt>, <tt>beta</tt>, <tt>gamma</tt>.
71 protected abstract String getId();
75 * @return globally unique ID string.
77 protected abstract String getInstanceUUID();
80 * Execute REST request.
81 * @param request request data.
82 * @param http HTTP request.
83 * @return response data.
84 * @throws UnirestException REST error.
86 @RequestMapping(value = "/invoke",
87 method = RequestMethod.POST,
88 consumes = MediaType.APPLICATION_JSON_VALUE,
89 produces = MediaType.APPLICATION_JSON_VALUE)
90 public Response execute(final Request request,
91 final HttpServletRequest http) throws UnirestException {
93 final ONAPLogAdapter adapter = new ONAPLogAdapter(this.getLogger());
97 adapter.entering(new ONAPLogAdapter.HttpServletRequestAdapter(http));
99 final Response response = new Response();
100 response.setService(request.getService());
101 final String code = StringUtils.defaultString(request.getCode(), "OK").toUpperCase();
102 response.setCode(this.getId() + "." + code);
103 response.setSeverity(StringUtils.defaultString(request.getSeverity(), "INFO"));
105 for (final Request target : request.getRequests()) {
106 final Response targetResponse = this.executeDelegate(target, http, adapter);
107 response.getResponses().add(targetResponse);
118 * Set in-process mode, for unit testing.
120 static void setInProcess() {
126 * @param request to be executed.
127 * @param http incoming HTTP request.
128 * @param logger logging adapter.
131 private Response executeDelegate(final Request request,
132 final HttpServletRequest http,
133 final ONAPLogAdapter logger) {
144 return this.executeInProcess(request, logger);
147 return this.executeREST(request, http, logger);
149 catch (final UnirestException | ReflectiveOperationException e) {
150 logger.unwrap().error("Execute error", e);
151 final Response response = new Response();
152 response.setCode((this.getServiceName() + ".INVOKE_ERROR").toUpperCase(Locale.getDefault()));
153 response.setSeverity("ERROR");
159 * Execute invocation over REST.
160 * @param request mock request to be executed.
161 * @param http HTTP request, used (only) to address the outgoing request.
162 * @param logger logger adapter.
163 * @return invocation response.
164 * @throws UnirestException REST error.
166 private Response executeREST(final Request request,
167 final HttpServletRequest http,
168 final ONAPLogAdapter logger) throws UnirestException {
169 // Otherwise via REST.
171 logger.unwrap().info("Sending:\n{}", request);
172 final StringBuilder url = new StringBuilder();
173 url.append(http.getProtocol()).append("://");
174 url.append(http.getServerName()).append(':');
175 url.append(http.getServerPort()).append("/services/").append(request.getService());
177 final UUID invocationID = logger.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
178 final HttpResponse<JsonNode> response =
179 Unirest.post(url.toString())
180 .header(ONAPLogConstants.Headers.REQUEST_ID, MDC.get(ONAPLogConstants.MDCs.REQUEST_ID))
181 .header(ONAPLogConstants.Headers.INVOCATION_ID, invocationID.toString())
182 .header(ONAPLogConstants.Headers.PARTNER_NAME, this.getServiceName())
183 .header("Accept", MediaType.APPLICATION_JSON_VALUE)
184 .header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
190 final JSONObject responseJSON = response.getBody().getObject();
191 logger.unwrap().info("Received:\n{}", responseJSON);
192 return Response.fromJSON(responseJSON);
196 * Execute request in-process.
197 * @param request mock request to be executed.
198 * @param logger logger adapter.
199 * @return invocation response.
200 * @throws ReflectiveOperationException error loading target class.
201 * @throws UnirestException REST error.
203 private Response executeInProcess(final Request request,
204 final ONAPLogAdapter logger) throws ReflectiveOperationException, UnirestException {
206 logger.unwrap().info("Executing in-process:\n{}", request);
208 // Derive the name of the delegate class.
210 final String delegateClass
211 = AbstractComponent.class.getPackage().getName() + "." + request.getService()
212 + ".Component" + request.getService().substring(0, 1).toUpperCase()
213 + request.getService().substring(1);
214 logger.unwrap().info("Invoking in-process [{}].", delegateClass);
215 final AbstractComponent component = (AbstractComponent)Class.forName(delegateClass).newInstance();
217 // Using Spring mock since we're not *actually* going over HTTP.
219 final MockHttpServletRequest mock = new MockHttpServletRequest();
221 // Generate INVOCATION_ID, and set MDCs aside for safekeeping.
222 // (This is because when mocking, everything happens in the same thread.)
224 final UUID invocationID = logger.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
225 final String requestID = MDC.get(ONAPLogConstants.MDCs.REQUEST_ID);
226 final Map<String, String> safekeeping = MDC.getCopyOfContextMap();
230 mock.addHeader(ONAPLogConstants.Headers.REQUEST_ID, StringUtils.defaultString(requestID));
231 mock.addHeader(ONAPLogConstants.Headers.INVOCATION_ID, invocationID.toString());
232 mock.addHeader(ONAPLogConstants.Headers.PARTNER_NAME, this.getServiceName());
240 return component.execute(request, mock);
246 safekeeping.forEach((k, v) -> MDC.put(k, v));
251 * Ensure non-nullness.
252 * @param in to be checked.
254 * @return input value, not null.
256 private static <T> T notNull(final T in) {
258 throw new AssertionError("");
264 * Get service name, with default.
265 * @return service name, suitable for logging as MDC.
267 private String getServiceName() {
268 return "service." + StringUtils.defaultString(this.getId(), "unnamed");
272 * Get logger instance.
275 private Logger getLogger() {
276 return LoggerFactory.getLogger(this.getClass());