001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    
020    package org.crsh.util;
021    
022    import org.crsh.shell.ErrorKind;
023    import org.crsh.shell.impl.command.spi.CommandException;
024    import org.crsh.plugin.PluginContext;
025    import org.crsh.plugin.ResourceKind;
026    import org.crsh.vfs.Resource;
027    
028    import java.io.UnsupportedEncodingException;
029    import java.util.Map;
030    import java.util.concurrent.ConcurrentHashMap;
031    
032    public class ClassCache<T> {
033    
034      /** . */
035      private final ClassFactory<T> classFactory;
036    
037      /** . */
038      private final Map<String, TimestampedObject<Class<? extends T>>> classes = new ConcurrentHashMap<String, TimestampedObject<Class<? extends T>>>();
039    
040      /** . */
041      private final PluginContext context;
042    
043      /** . */
044      private final ResourceKind kind;
045    
046      public ClassCache(PluginContext context, ClassFactory<T> classFactory, ResourceKind kind) {
047        this.classFactory = classFactory;
048        this.context = context;
049        this.kind = kind;
050      }
051    
052      private TimestampedObject<Class<? extends T>> loadClass(String name) {
053        return classes.get(name);
054      }
055    
056      private void saveClass(String name, TimestampedObject<Class<? extends T>> clazz) {
057        classes.put(name, clazz);
058      }
059    
060      private Resource getResource(String name) {
061        return context.loadResource(name, kind);
062      }
063    
064      public TimestampedObject<Class<? extends T>> getClass(String name) throws CommandException, NullPointerException {
065        if (name == null) {
066          throw new NullPointerException("No null argument allowed");
067        }
068    
069        TimestampedObject<Class<? extends T>> providerRef = loadClass(name);
070    
071        //
072        Resource script = getResource(name);
073    
074        //
075        if (script != null) {
076          if (providerRef != null) {
077            if (script.getTimestamp() != providerRef.getTimestamp()) {
078              providerRef = null;
079            }
080          }
081    
082          //
083          if (providerRef == null) {
084    
085            //
086            String source;
087            try {
088              source = new String(script.getContent(), "UTF-8");
089            }
090            catch (UnsupportedEncodingException e) {
091              throw new CommandException(ErrorKind.INTERNAL, "Could not compile command script " + name, e);
092            }
093    
094            //
095            Class<? extends T> clazz = classFactory.parse(name, source);
096            providerRef = new TimestampedObject<Class<? extends T>>(script.getTimestamp(), clazz);
097            saveClass(name, providerRef);
098          }
099        }
100    
101        //
102        if (providerRef == null) {
103          return null;
104        }
105    
106        //
107        return providerRef;
108      }
109    }