001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.impl;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.LinkedHashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.ConcurrentMap;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.Endpoint;
030import org.apache.camel.impl.validator.ValidatorKey;
031import org.apache.camel.model.validator.ValidatorDefinition;
032import org.apache.camel.spi.DataType;
033import org.apache.camel.spi.EndpointRegistry;
034import org.apache.camel.spi.Validator;
035import org.apache.camel.spi.ValidatorRegistry;
036import org.apache.camel.util.CamelContextHelper;
037import org.apache.camel.util.LRUCache;
038import org.apache.camel.util.ObjectHelper;
039import org.apache.camel.util.ServiceHelper;
040
041/**
042 * Default implementation of {@link org.apache.camel.spi.ValidatorRegistry}.
043 */
044public class DefaultValidatorRegistry extends LRUCache<ValidatorKey, Validator> implements ValidatorRegistry<ValidatorKey> {
045    private static final long serialVersionUID = 1L;
046    private ConcurrentMap<ValidatorKey, Validator> staticMap;
047    private final CamelContext context;
048
049    public DefaultValidatorRegistry(CamelContext context) throws Exception {
050        this(context, new ArrayList<>());
051    }
052
053    public DefaultValidatorRegistry(CamelContext context, List<ValidatorDefinition> definitions) throws Exception {
054        // do not stop on eviction, as the validator may still be in use
055        super(CamelContextHelper.getMaximumValidatorCacheSize(context), CamelContextHelper.getMaximumValidatorCacheSize(context), false);
056        // static map to hold validator we do not want to be evicted
057        this.staticMap = new ConcurrentHashMap<>();
058        this.context = context;
059        
060        for (ValidatorDefinition def : definitions) {
061            Validator validator = def.createValidator(context);
062            context.addService(validator);
063            put(new ValidatorKey(new DataType(def.getType())), validator);
064        }
065    }
066
067    public Validator resolveValidator(ValidatorKey key) {
068        Validator answer = get(key);
069        if (answer == null && ObjectHelper.isNotEmpty(key.getType().getName())) {
070            answer = get(new ValidatorKey(new DataType(key.getType().getModel())));
071        }
072        return answer;
073    }
074
075    @Override
076    public void start() throws Exception {
077        resetStatistics();
078    }
079
080    @Override
081    public Validator get(Object o) {
082        // try static map first
083        Validator answer = staticMap.get(o);
084        if (answer == null) {
085            answer = super.get(o);
086        } else {
087            hits.incrementAndGet();
088        }
089        return answer;
090    }
091
092    @Override
093    public Validator put(ValidatorKey key, Validator validator) {
094        // at first we must see if the key already exists and then replace it back, so it stays the same spot
095        Validator answer = staticMap.remove(key);
096        if (answer != null) {
097            // replace existing
098            staticMap.put(key, validator);
099            return answer;
100        }
101
102        answer = super.remove(key);
103        if (answer != null) {
104            // replace existing
105            super.put(key, validator);
106            return answer;
107        }
108
109        // we want validators to be static if they are part of setting up or starting routes
110        if (context.isSetupRoutes() || context.isStartingRoutes()) {
111            answer = staticMap.put(key, validator);
112        } else {
113            answer = super.put(key, validator);
114        }
115
116        return answer;
117    }
118
119    @Override
120    public void putAll(Map<? extends ValidatorKey, ? extends Validator> map) {
121        // need to use put instead of putAll to ensure the entries gets added to either static or dynamic map
122        for (Map.Entry<? extends ValidatorKey, ? extends Validator> entry : map.entrySet()) {
123            put(entry.getKey(), entry.getValue());
124        }
125    }
126
127    @Override
128    public boolean containsKey(Object o) {
129        return staticMap.containsKey(o) || super.containsKey(o);
130    }
131
132    @Override
133    public boolean containsValue(Object o) {
134        return staticMap.containsValue(o) || super.containsValue(o);
135    }
136
137    @Override
138    public int size() {
139        return staticMap.size() + super.size();
140    }
141
142    public int staticSize() {
143        return staticMap.size();
144    }
145
146    @Override
147    public int dynamicSize() {
148        return super.size();
149    }
150
151    @Override
152    public boolean isEmpty() {
153        return staticMap.isEmpty() && super.isEmpty();
154    }
155
156    @Override
157    public Validator remove(Object o) {
158        Validator answer = staticMap.remove(o);
159        if (answer == null) {
160            answer = super.remove(o);
161        }
162        return answer;
163    }
164
165    @Override
166    public void clear() {
167        staticMap.clear();
168        super.clear();
169    }
170
171    @Override
172    public Set<ValidatorKey> keySet() {
173        Set<ValidatorKey> answer = new LinkedHashSet<>();
174        answer.addAll(staticMap.keySet());
175        answer.addAll(super.keySet());
176        return answer;
177    }
178
179    @Override
180    public Collection<Validator> values() {
181        Collection<Validator> answer = new ArrayList<>();
182        answer.addAll(staticMap.values());
183        answer.addAll(super.values());
184        return answer;
185    }
186
187    @Override
188    public Set<Entry<ValidatorKey, Validator>> entrySet() {
189        Set<Entry<ValidatorKey, Validator>> answer = new LinkedHashSet<>();
190        answer.addAll(staticMap.entrySet());
191        answer.addAll(super.entrySet());
192        return answer;
193    }
194
195    @Override
196    public int getMaximumCacheSize() {
197        return super.getMaxCacheSize();
198    }
199
200    /**
201     * Purges the cache
202     */
203    @Override
204    public void purge() {
205        // only purge the dynamic part
206        super.clear();
207    }
208
209    @Override
210    public boolean isStatic(DataType type) {
211        return staticMap.containsKey(new ValidatorKey(type));
212    }
213
214    @Override
215    public boolean isDynamic(DataType type) {
216        return super.containsKey(new ValidatorKey(type));
217    }
218
219    @Override
220    public void stop() throws Exception {
221        ServiceHelper.stopServices(staticMap.values());
222        ServiceHelper.stopServices(values());
223        purge();
224    }
225
226    @Override
227    public String toString() {
228        return "ValidatorRegistry for " + context.getName() + ", capacity: " + getMaxCacheSize();
229    }
230
231}