| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.camel.component.servicenow; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import javax.ws.rs.core.HttpHeaders; |
| import javax.ws.rs.core.Response; |
| |
| import com.fasterxml.jackson.databind.JavaType; |
| import com.fasterxml.jackson.databind.JsonNode; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import org.apache.camel.Exchange; |
| import org.apache.camel.Message; |
| import org.apache.camel.Processor; |
| import org.apache.camel.util.ObjectHelper; |
| |
| public abstract class AbstractServiceNowProcessor implements Processor { |
| |
| protected final ServiceNowEndpoint endpoint; |
| protected final ServiceNowConfiguration config; |
| protected final ObjectMapper mapper; |
| protected final ServiceNowClient client; |
| |
| // Cache for JavaTypes |
| private final JavaTypeCache javaTypeCache; |
| private final List<ServiceNowDispatcher> dispatchers; |
| |
| protected AbstractServiceNowProcessor(ServiceNowEndpoint endpoint) throws Exception { |
| this.javaTypeCache = new JavaTypeCache(); |
| this.endpoint = endpoint; |
| this.config = endpoint.getConfiguration(); |
| this.mapper = config.getOrCreateMapper(); |
| this.client = new ServiceNowClient(endpoint.getCamelContext(), config); |
| this.dispatchers = new ArrayList<>(); |
| } |
| |
| protected AbstractServiceNowProcessor setBodyAndHeaders(Message message, Class<?> responseModel, Response response) throws Exception { |
| if (response != null) { |
| setHeaders(message, responseModel, response); |
| setBody(message, responseModel, response); |
| } |
| |
| return this; |
| } |
| |
| @Override |
| public void process(Exchange exchange) throws Exception { |
| final ServiceNowDispatcher dispatcher = findDispatcher(exchange); |
| |
| if (dispatcher != null) { |
| dispatcher.process(exchange); |
| } else { |
| throw new IllegalArgumentException("Unable to process exchange"); |
| } |
| } |
| |
| // ********************************* |
| // Header |
| // ********************************* |
| |
| protected AbstractServiceNowProcessor setHeaders(Message message, Class<?> responseModel, Response response) throws Exception { |
| ServiceNowHelper.findOffsets(response, (k, v) -> message.setHeader(k, v)); |
| |
| String attachmentMeta = response.getHeaderString(ServiceNowConstants.ATTACHMENT_META_HEADER); |
| if (ObjectHelper.isNotEmpty(attachmentMeta)) { |
| message.setHeader( |
| ServiceNowConstants.CONTENT_META, |
| mapper.readValue(attachmentMeta, Map.class) |
| ); |
| } |
| |
| copyHeader(response, HttpHeaders.CONTENT_TYPE, message, ServiceNowConstants.CONTENT_TYPE); |
| copyHeader(response, HttpHeaders.CONTENT_ENCODING, message, ServiceNowConstants.CONTENT_ENCODING); |
| |
| if (responseModel != null) { |
| message.getHeaders().putIfAbsent(ServiceNowConstants.MODEL, responseModel.getName()); |
| message.getHeaders().putIfAbsent(ServiceNowConstants.RESPONSE_MODEL, responseModel.getName()); |
| } |
| |
| return this; |
| } |
| |
| // ********************************* |
| // Body |
| // ********************************* |
| |
| protected AbstractServiceNowProcessor setBody(Message message, Class<?> model, Response response) throws Exception { |
| if (message != null && response != null) { |
| if (ObjectHelper.isNotEmpty(response.getHeaderString(HttpHeaders.CONTENT_TYPE))) { |
| |
| JsonNode root = response.readEntity(JsonNode.class); |
| Map<String, String> responseAttributes = null; |
| |
| if (root != null) { |
| Iterator<Map.Entry<String, JsonNode>> fields = root.fields(); |
| while (fields.hasNext()) { |
| final Map.Entry<String, JsonNode> entry = fields.next(); |
| final String key = entry.getKey(); |
| final JsonNode node = entry.getValue(); |
| |
| if (ObjectHelper.equal("result", key, true)) { |
| Object body = unwrap(node, model); |
| if (body != null) { |
| message.setHeader(ServiceNowConstants.RESPONSE_TYPE, body.getClass()); |
| message.setBody(body); |
| } |
| } else { |
| if (responseAttributes == null) { |
| responseAttributes = new HashMap<>(); |
| } |
| |
| responseAttributes.put(key, node.textValue()); |
| } |
| } |
| |
| if (responseAttributes != null) { |
| message.setHeader(ServiceNowConstants.RESPONSE_META, responseAttributes); |
| } |
| } |
| } |
| } |
| |
| return this; |
| } |
| |
| protected AbstractServiceNowProcessor validateBody(Message message, Class<?> model) { |
| return validateBody(message.getBody(), model); |
| } |
| |
| protected AbstractServiceNowProcessor validateBody(Object body, Class<?> model) { |
| ObjectHelper.notNull(body, "body"); |
| |
| if (!body.getClass().isAssignableFrom(model)) { |
| throw new IllegalArgumentException( |
| "Body is not compatible with model (body=" + body.getClass() + ", model=" + model); |
| } |
| |
| return this; |
| } |
| |
| protected Object unwrap(JsonNode node, Class<?> model) throws IOException { |
| if (model == String.class) { |
| // If the model is a String, let's just serialize it as |
| // a json string |
| return mapper.writeValueAsString(node); |
| } |
| |
| final Object result; |
| |
| if (node.isArray()) { |
| if (model.isInstance(Map.class)) { |
| // If the model is a Map, there's no need to use any |
| // specific JavaType to instruct Jackson about the |
| // expected element type |
| result = mapper.treeToValue(node, List.class); |
| } else { |
| result = mapper.readValue(node.traverse(), javaTypeCache.get(model)); |
| } |
| } else { |
| result = mapper.treeToValue(node, model); |
| } |
| |
| return result; |
| } |
| |
| // ********************************* |
| // Helpers |
| // ********************************* |
| |
| protected final void addDispatcher(ServiceNowDispatcher dispatcher) { |
| this.dispatchers.add(dispatcher); |
| } |
| |
| protected final void addDispatcher(String action, Processor processor) { |
| addDispatcher(ServiceNowDispatcher.on(action, null, processor)); |
| } |
| |
| protected final void addDispatcher(String action, String subject, Processor processor) { |
| addDispatcher(ServiceNowDispatcher.on(action, subject, processor)); |
| } |
| |
| protected final ServiceNowDispatcher findDispatcher(Exchange exchange) { |
| ServiceNowDispatcher dispatcher = null; |
| for (int i = 0; i < dispatchers.size(); i++) { |
| dispatcher = dispatchers.get(i); |
| if (dispatcher.match(exchange)) { |
| return dispatcher; |
| } |
| } |
| |
| return null; |
| } |
| |
| // ********************************* |
| // Helpers |
| // ********************************* |
| |
| protected Object getRequestParamFromHeader(ServiceNowParam sysParam, Message message) { |
| return message.getHeader( |
| sysParam.getHeader(), |
| sysParam.getDefaultValue(config), |
| sysParam.getType() |
| ); |
| } |
| |
| protected Object getMandatoryRequestParamFromHeader(ServiceNowParam sysParam, Message message) { |
| return ObjectHelper.notNull( |
| getRequestParamFromHeader(sysParam, message), |
| sysParam.getHeader() |
| ); |
| } |
| |
| protected void copyHeader(Response from, String fromId, Message to, String toId) { |
| Object fromValue = from.getHeaders().getFirst(fromId); |
| if (ObjectHelper.isNotEmpty(fromValue)) { |
| to.setHeader(toId, fromValue); |
| } |
| } |
| |
| protected Class<?> getRequestModel(Message message) { |
| return getRequestModel(message, null); |
| } |
| |
| protected Class<?> getRequestModel(Message message, String modelName) { |
| Class<?> model = null; |
| |
| if (message != null) { |
| model = message.getHeader(ServiceNowConstants.REQUEST_MODEL, Class.class); |
| if (model == null) { |
| model = message.getHeader(ServiceNowConstants.MODEL, Class.class); |
| } |
| } |
| |
| return model != null |
| ? model |
| : ObjectHelper.isEmpty(modelName) ? Map.class : config.getRequestModel(modelName, Map.class); |
| } |
| |
| protected Class<?> getResponseModel(Message message) { |
| return getRequestModel(message, null); |
| } |
| |
| protected Class<?> getResponseModel(Message message, String modelName) { |
| Class<?> model = null; |
| |
| if (message != null) { |
| model = message.getHeader(ServiceNowConstants.RESPONSE_MODEL, Class.class); |
| if (model == null) { |
| model = message.getHeader(ServiceNowConstants.MODEL, Class.class); |
| } |
| } |
| |
| return model != null |
| ? model |
| : ObjectHelper.isEmpty(modelName) ? Map.class : config.getResponseModel(modelName, Map.class); |
| } |
| |
| protected String getApiVersion(Message message) { |
| return message.getHeader(ServiceNowConstants.API_VERSION, config.getApiVersion(), String.class); |
| } |
| |
| protected String getTableName(Message message) { |
| return message.getHeader(ServiceNowParams.PARAM_TABLE_NAME.getHeader(), config.getTable(), String.class); |
| } |
| |
| protected String getSysID(Message message) { |
| return message.getHeader(ServiceNowParams.PARAM_SYS_ID.getHeader(), String.class); |
| } |
| |
| // ************************************************************************* |
| // Use ClassValue to lazy create and cache JavaType |
| // ************************************************************************* |
| |
| private class JavaTypeCache extends ClassValue<JavaType> { |
| @Override |
| protected JavaType computeValue(Class<?> type) { |
| return mapper.getTypeFactory().constructCollectionType(List.class, type); |
| } |
| } |
| } |