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.converter;
018
019import java.beans.PropertyEditor;
020import java.beans.PropertyEditorManager;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.apache.camel.Exchange;
025import org.apache.camel.TypeConverter;
026import org.apache.camel.util.LRUCacheFactory;
027import org.apache.camel.util.ObjectHelper;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * Uses the {@link java.beans.PropertyEditor} conversion system to convert Objects to
033 * and from String values.
034 *
035 * @deprecated should be removed as it can cause side-effects when using 3rd party property editors
036 *
037 * @version 
038 */
039@Deprecated
040public class PropertyEditorTypeConverter implements TypeConverter {
041
042    private static final Logger LOG = LoggerFactory.getLogger(PropertyEditorTypeConverter.class);
043    // use a soft bound cache to avoid using too much memory in case a lot of different classes
044    // is being converted to string
045    @SuppressWarnings("unchecked")
046    private final Map<Class<?>, Class<?>> misses = LRUCacheFactory.newLRUSoftCache(1000);
047    // we don't anticipate so many property editors so we have unbounded map
048    private final Map<Class<?>, PropertyEditor> cache = new HashMap<>();
049
050    public void clear() {
051        cache.clear();
052        misses.clear();
053    }
054
055    @Override
056    public boolean allowNull() {
057        return false;
058    }
059
060    @Override
061    public <T> T convertTo(Class<T> type, Object value) {
062        // We can't convert null values since we can't figure out a property
063        // editor for it.
064        if (value == null) {
065            return null;
066        }
067
068        if (value.getClass() == String.class) {
069            // No conversion needed.
070            if (type == String.class) {
071                return ObjectHelper.cast(type, value);
072            }
073
074            Class<?> key = type;
075            PropertyEditor editor = lookupEditor(key);
076            if (editor != null) {
077                // we are essentially not thread safe as we use 2 calls to convert
078                editor.setAsText(value.toString());
079                return ObjectHelper.cast(type, editor.getValue());
080            }
081        } else if (type == String.class) {
082            Class<?> key = value.getClass();
083            PropertyEditor editor = lookupEditor(key);
084            if (editor != null) {
085                // we are essentially not thread safe as we use 2 calls to convert
086                editor.setValue(value);
087                return ObjectHelper.cast(type, editor.getAsText());
088            }
089        }
090
091        return null;
092    }
093
094    private PropertyEditor lookupEditor(Class<?> type) {
095        // check misses first
096        if (misses.containsKey(type)) {
097            LOG.trace("No previously found property editor for type: {}", type);
098            return null;
099        }
100
101        synchronized (cache) {
102            // not a miss then try to lookup the editor
103            PropertyEditor editor = cache.get(type);
104            if (editor == null) {
105                // findEditor is synchronized and very slow so we want to only lookup once for a given key
106                // and then we use our own local cache for faster lookup
107                editor = PropertyEditorManager.findEditor(type);
108
109                // either we found an editor, or if not then register it as a miss
110                if (editor != null) {
111                    LOG.trace("Found property editor for type: {} -> {}", type, editor);
112                    cache.put(type, editor);
113                } else {
114                    LOG.trace("Cannot find property editor for type: {}", type);
115                    misses.put(type, type);
116                }
117            }
118            return editor;
119        }
120    }
121
122    @Override
123    public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
124        return convertTo(type, value);
125    }
126
127    @Override
128    public <T> T mandatoryConvertTo(Class<T> type, Object value) {
129        return convertTo(type, value);
130    }
131
132    @Override
133    public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) {
134        return convertTo(type, value);
135    }
136
137    @Override
138    public <T> T tryConvertTo(Class<T> type, Exchange exchange, Object value) {
139        try {
140            return convertTo(type, exchange, value);
141        } catch (Exception e) {
142            return null;
143        }
144    }
145
146    @Override
147    public <T> T tryConvertTo(Class<T> type, Object value) {
148        try {
149            return convertTo(type, null, value);
150        } catch (Exception e) {
151            return null;
152        }
153    }
154}