001// Copyright 2004, 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.hivemind.impl;
016
017import java.lang.reflect.Constructor;
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.apache.hivemind.ApplicationRuntimeException;
026import org.apache.hivemind.ErrorHandler;
027import org.apache.hivemind.Location;
028import org.apache.hivemind.internal.RegistryInfrastructure;
029import org.apache.hivemind.schema.Translator;
030import org.apache.hivemind.schema.rules.ClassTranslator;
031import org.apache.hivemind.schema.rules.InstanceTranslator;
032import org.apache.hivemind.schema.rules.ServiceTranslator;
033import org.apache.hivemind.schema.rules.SmartTranslator;
034
035/**
036 * Manages translators for {@link org.apache.hivemind.impl.RegistryInfrastructureImpl}.
037 * 
038 * @author Howard Lewis Ship
039 */
040public class TranslatorManager
041{
042    static final Log LOG = LogFactory.getLog(TranslatorManager.class);
043
044    public static final String TRANSLATORS_CONFIGURATION_ID = "hivemind.Translators";
045
046    private ErrorHandler _errorHandler;
047
048    private RegistryInfrastructure _registry;
049
050    /**
051     * Map of Class, keyed on translator name, used to instantiate new
052     * {@link org.apache.hivemind.schema.Translator}s. Loaded from the
053     * <code>hivemind.Translators</code> configuration point;
054     */
055    private Map _translatorClasses = new HashMap();
056
057    private Map _translatorsCache = new HashMap();
058
059    private boolean _translatorsLoaded;
060
061    public TranslatorManager(RegistryInfrastructure registry, ErrorHandler errorHandler)
062    {
063        _registry = registry;
064        _errorHandler = errorHandler;
065
066        // Seed the basic translators used to "bootstrap" the
067        // processing of the hivemind.Translators configuration point.
068
069        _translatorsCache.put("class", new ClassTranslator());
070        _translatorsCache.put("service", new ServiceTranslator());
071        _translatorsCache.put("smart", new SmartTranslator());
072        _translatorsCache.put("instance", new InstanceTranslator());
073
074        // smart may take an initializer, so we need to put it into the classes as
075        // well.
076
077        _translatorClasses.put("smart", SmartTranslator.class);
078
079    }
080
081    public synchronized Translator getTranslator(String constructor)
082    {
083        // The cache is preloaded with the hardcoded translators.
084
085        if (!_translatorsLoaded && !_translatorsCache.containsKey(constructor))
086            loadTranslators();
087
088        Translator result = (Translator) _translatorsCache.get(constructor);
089
090        if (result == null)
091        {
092            result = constructTranslator(constructor);
093            _translatorsCache.put(constructor, result);
094        }
095
096        return result;
097    }
098
099    private Translator constructTranslator(String constructor)
100    {
101        String name = constructor;
102        String initializer = null;
103
104        int commax = constructor.indexOf(',');
105
106        if (commax > 0)
107        {
108            name = constructor.substring(0, commax);
109            initializer = constructor.substring(commax + 1);
110        }
111
112        Class translatorClass = findTranslatorClass(name);
113
114        // TODO: check for null class, meaning that the translator is a service.
115
116        return createTranslator(translatorClass, initializer);
117    }
118
119    private Translator createTranslator(Class translatorClass, String initializer)
120    {
121        try
122        {
123
124            if (initializer == null)
125                return (Translator) translatorClass.newInstance();
126
127            Constructor c = translatorClass.getConstructor(new Class[]
128            { String.class });
129
130            return (Translator) c.newInstance(new Object[]
131            { initializer });
132        }
133        catch (Exception ex)
134        {
135            throw new ApplicationRuntimeException(ImplMessages.translatorInstantiationFailure(
136                    translatorClass,
137                    ex), ex);
138        }
139    }
140
141    private Class findTranslatorClass(String translatorName)
142    {
143        Class result = (Class) _translatorClasses.get(translatorName);
144
145        if (result == null)
146            throw new ApplicationRuntimeException(ImplMessages.unknownTranslatorName(
147                    translatorName,
148                    TRANSLATORS_CONFIGURATION_ID));
149
150        return result;
151    }
152
153    private void loadTranslators()
154    {
155        // Prevent endless recursion!
156
157        _translatorsLoaded = true;
158
159        List contributions = _registry.getConfiguration(TRANSLATORS_CONFIGURATION_ID, null);
160
161        Map locations = new HashMap();
162        locations.put("class", null);
163
164        Iterator i = contributions.iterator();
165        while (i.hasNext())
166        {
167            TranslatorContribution c = (TranslatorContribution) i.next();
168
169            String name = c.getName();
170            Location oldLocation = (Location) locations.get(name);
171
172            if (oldLocation != null)
173            {
174                _errorHandler.error(LOG, ImplMessages.duplicateTranslatorName(name, oldLocation), c
175                        .getLocation(), null);
176
177                continue;
178            }
179
180            locations.put(name, c.getLocation());
181
182            Translator t = c.getTranslator();
183
184            if (t != null)
185            {
186                _translatorsCache.put(name, t);
187                continue;
188            }
189
190            Class tClass = c.getTranslatorClass();
191
192            if (tClass == null)
193            {
194                _errorHandler.error(
195                        LOG,
196                        ImplMessages.incompleteTranslator(c),
197                        c.getLocation(),
198                        null);
199                continue;
200            }
201
202            _translatorClasses.put(name, tClass);
203        }
204
205    }
206
207}