/**
 * 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.util.Collection;
import java.util.List;
import java.util.Map;

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 ServiceNowProcessor<T> implements Processor {
    // Cache for JavaTypes
    private final CollectionValue<List> javaTypeCache;

    protected final ServiceNowEndpoint endpoint;
    protected final ServiceNowConfiguration config;
    protected final T client;
    protected final ObjectMapper mapper;

    protected ServiceNowProcessor(ServiceNowEndpoint endpoint, Class<T> type) throws Exception {
        this.javaTypeCache = new CollectionValue<>(List.class);
        this.endpoint = endpoint;
        this.config = endpoint.getConfiguration();
        this.client = endpoint.createClient(type);
        this.mapper = ObjectHelper.notNull(config.getMapper(), "mapper");
    }

    @Override
    public void process(Exchange exchange) throws Exception {
        final Message in = exchange.getIn();
        final String tableName = in.getHeader(ServiceNowConstants.TABLE, config.getTable(), String.class);
        final Class<?> model = in.getHeader(ServiceNowConstants.MODEL, config.getModel(tableName, Map.class), Class.class);
        final String action = in.getHeader(ServiceNowConstants.ACTION, String.class);
        final String sysId = in.getHeader(ServiceNowConstants.SYSPARM_ID, String.class);

        doProcess(
            exchange,
            ObjectHelper.notNull(model, "model"),
            ObjectHelper.notNull(action, "action"),
            ObjectHelper.notNull(tableName, "tableName"),
            sysId);
    }

    protected abstract void doProcess(
        Exchange exchange,
        Class<?> model,
        String action,
        String tableName,
        String sysId) throws Exception;


    protected void setBody(Message message, Class<?> model, JsonNode answer) throws Exception {
        message.setBody(unwrap(answer, model));
    }

    protected Object unwrap(JsonNode answer, Class<?> model) throws Exception {
        Object result = null;

        if (answer != null) {
            JsonNode node = answer.get("result");
            if (node != null) {
                if (model == null) {
                    result = mapper.writeValueAsString(node);
                } else {
                    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;
    }

    protected void validateBody(Message message, Class<?> model) {
        validateBody(message.getBody(), model);
    }

    protected void 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);
        }
    }

    private class CollectionValue<T extends Collection> extends ClassValue<JavaType> {
        private final Class<T> collectionType;

        public CollectionValue(Class<T> collectionType) {
            this.collectionType = collectionType;
        }

        @Override
        protected JavaType computeValue(Class<?> type) {
            return mapper.getTypeFactory().constructCollectionType(collectionType, type);
        }
    }
}

@@ -849,37 +849,8 @@
 w;%0A%0A
-import java.util.Collection;%0A
 impo
@@ -1302,29 +1302,21 @@
 nal 
-CollectionValue%3CList%3E
+JavaTypeCache
  jav
@@ -1635,36 +1635,22 @@
 new 
-CollectionValue%3C%3E(List.class
+JavaTypeCache(
 );%0A 
@@ -3180,136 +3180,8 @@
 ) %7B%0A
-                if (model == null) %7B%0A                    result = mapper.writeValueAsString(node);%0A                %7D else %7B%0A    
     
@@ -3230,28 +3230,24 @@
             
-   
- 
 if (model.is
@@ -3265,28 +3265,24 @@
 p.class)) %7B%0A
-    
             
@@ -3370,20 +3370,16 @@
         
-    
 // speci
@@ -3445,20 +3445,16 @@
         
-   
- 
 // expec
@@ -3470,20 +3470,16 @@
 nt type%0A
-    
         
@@ -3553,36 +3553,32 @@
                 
-    
 %7D else %7B%0A       
@@ -3586,36 +3586,32 @@
                 
-   
- 
 result = mapper.
@@ -3680,34 +3680,26 @@
             
-    %7D%0A    
+%7D%0A
             
@@ -3723,36 +3723,32 @@
                 
-   
- 
 result = mapper.
@@ -3773,38 +3773,16 @@
 model);%0A
-                    %7D%0A
         
@@ -4319,45 +4319,21 @@
 ass 
-CollectionValue%3CT extends Collection%3E
+JavaTypeCache
  ext
@@ -4364,175 +4364,8 @@
 %3E %7B%0A
-        private final Class%3CT%3E collectionType;%0A%0A        public CollectionValue(Class%3CT%3E collectionType) %7B%0A            this.collectionType = collectionType;%0A        %7D%0A%0A
     
@@ -4506,22 +4506,18 @@
 ype(
-collectionType
+List.class
 , ty

Improve action processor