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.impl.validator.ValidatorKey;
030import org.apache.camel.model.validator.ValidatorDefinition;
031import org.apache.camel.spi.DataType;
032import org.apache.camel.spi.Validator;
033import org.apache.camel.spi.ValidatorRegistry;
034import org.apache.camel.util.CamelContextHelper;
035import org.apache.camel.util.LRUCache;
036import org.apache.camel.util.ObjectHelper;
037import org.apache.camel.util.ServiceHelper;
038
039/**
040 * Default implementation of {@link org.apache.camel.spi.ValidatorRegistry}.
041 */
042public class DefaultValidatorRegistry extends LRUCache<ValidatorKey, Validator> implements ValidatorRegistry<ValidatorKey> {
043    private static final long serialVersionUID = 1L;
044    private ConcurrentMap<ValidatorKey, Validator> staticMap;
045    private final CamelContext context;
046
047    public DefaultValidatorRegistry(CamelContext context) throws Exception {
048        this(context, new ArrayList<>());
049    }
050
051    public DefaultValidatorRegistry(CamelContext context, List<ValidatorDefinition> definitions) throws Exception {
052        // do not stop on eviction, as the validator may still be in use
053        super(CamelContextHelper.getMaximumValidatorCacheSize(context), CamelContextHelper.getMaximumValidatorCacheSize(context), false);
054        // static map to hold validator we do not want to be evicted
055        this.staticMap = new ConcurrentHashMap<>();
056        this.context = context;
057        
058        for (ValidatorDefinition def : definitions) {
059            Validator validator = def.createValidator(context);
060            context.addService(validator);
061            put(new ValidatorKey(new DataType(def.getType())), validator);
062        }
063    }
064
065    public Validator resolveValidator(ValidatorKey key) {
066        Validator answer = get(key);
067        if (answer == null && ObjectHelper.isNotEmpty(key.getType().getName())) {
068            answer = get(new ValidatorKey(new DataType(key.getType().getModel())));
069        }
070        return answer;
071    }
072
073    @Override
074    public void start() throws Exception {
075        resetStatistics();
076    }
077
078    @Override
079    public Validator get(Object o) {
080        // try static map first
081        Validator answer = staticMap.get(o);
082        if (answer == null) {
083            answer = super.get(o);
084        } else {
085            hits.increment();
086        }
087        return answer;
088    }
089
090    @Override
091    public Validator put(ValidatorKey key, Validator validator) {
092        // at first we must see if the key already exists and then replace it back, so it stays the same spot
093        Validator answer = staticMap.remove(key);
094        if (answer != null) {
095            // replace existing
096            staticMap.put(key, validator);
097            return answer;
098        }
099
100        answer = super.remove(key);
101        if (answer != null) {
102            // replace existing
103            super.put(key, validator);
104            return answer;
105        }
106
107        // we want validators to be static if they are part of setting up or starting routes
108        if (context.isSetupRoutes() || context.isStartingRoutes()) {
109            answer = staticMap.put(key, validator);
110        } else {
111            answer = super.put(key, validator);
112        }
113
114        return answer;
115    }
116
117    @Override
118    public void putAll(Map<? extends ValidatorKey, ? extends Validator> map) {
119        // need to use put instead of putAll to ensure the entries gets added to either static or dynamic map
120        for (Map.Entry<? extends ValidatorKey, ? extends Validator> entry : map.entrySet()) {
121            put(entry.getKey(), entry.getValue());
122        }
123    }
124
125    @Override
126    public boolean containsKey(Object o) {
127        return staticMap.containsKey(o) || super.containsKey(o);
128    }
129
130    @Override
131    public boolean containsValue(Object o) {
132        return staticMap.containsValue(o) || super.containsValue(o);
133    }
134
135    @Override
136    public int size() {
137        return staticMap.size() + super.size();
138    }
139
140    public int staticSize() {
141        return staticMap.size();
142    }
143
144    @Override
145    public int dynamicSize() {
146        return super.size();
147    }
148
149    @Override
150    public boolean isEmpty() {
151        return staticMap.isEmpty() && super.isEmpty();
152    }
153
154    @Override
155    public Validator remove(Object o) {
156        Validator answer = staticMap.remove(o);
157        if (answer == null) {
158            answer = super.remove(o);
159        }
160        return answer;
161    }
162
163    @Override
164    public void clear() {
165        staticMap.clear();
166        super.clear();
167    }
168
169    @Override
170    public Set<ValidatorKey> keySet() {
171        Set<ValidatorKey> answer = new LinkedHashSet<>();
172        answer.addAll(staticMap.keySet());
173        answer.addAll(super.keySet());
174        return answer;
175    }
176
177    @Override
178    public Collection<Validator> values() {
179        Collection<Validator> answer = new ArrayList<>();
180        answer.addAll(staticMap.values());
181        answer.addAll(super.values());
182        return answer;
183    }
184
185    @Override
186    public Set<Entry<ValidatorKey, Validator>> entrySet() {
187        Set<Entry<ValidatorKey, Validator>> answer = new LinkedHashSet<>();
188        answer.addAll(staticMap.entrySet());
189        answer.addAll(super.entrySet());
190        return answer;
191    }
192
193    @Override
194    public int getMaximumCacheSize() {
195        return super.getMaxCacheSize();
196    }
197
198    /**
199     * Purges the cache
200     */
201    @Override
202    public void purge() {
203        // only purge the dynamic part
204        super.clear();
205    }
206
207    @Override
208    public boolean isStatic(DataType type) {
209        return staticMap.containsKey(new ValidatorKey(type));
210    }
211
212    @Override
213    public boolean isDynamic(DataType type) {
214        return super.containsKey(new ValidatorKey(type));
215    }
216
217    @Override
218    public void stop() throws Exception {
219        ServiceHelper.stopServices(staticMap.values());
220        ServiceHelper.stopServices(values());
221        purge();
222    }
223
224    @Override
225    public String toString() {
226        return "ValidatorRegistry for " + context.getName() + ", capacity: " + getMaxCacheSize();
227    }
228
229}