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.plugin;
021    
022    import org.crsh.vfs.FS;
023    import org.crsh.vfs.File;
024    import org.crsh.vfs.Path;
025    import org.crsh.vfs.Resource;
026    
027    import java.io.ByteArrayOutputStream;
028    import java.io.IOException;
029    import java.util.ArrayList;
030    import java.util.Collections;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.SortedSet;
034    import java.util.TreeSet;
035    import java.util.logging.Level;
036    import java.util.logging.Logger;
037    import java.util.regex.Matcher;
038    import java.util.regex.Pattern;
039    
040    /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
041    public class ResourceManager {
042    
043      /** . */
044      private static final Pattern p = Pattern.compile("([^.]+)\\.[^.]+");
045    
046      /** . */
047      private static final Logger log = Logger.getLogger(ResourceManager.class.getName());
048    
049      /** . */
050      private final FS cmdFS;
051    
052      /** . */
053      private final FS confFS;
054    
055      /** . */
056      private volatile List<File> dirs;
057    
058      ResourceManager(FS cmdFS, FS confFS) {
059        this.cmdFS = cmdFS;
060        this.confFS = confFS;
061      }
062    
063      /**
064       * Load a resource from the context.
065       *
066       * @param resourceId the resource id
067       * @param resourceKind the resource kind
068       * @return the resource or null if it cannot be found
069       */
070      Iterable<Resource> loadResource(String resourceId, ResourceKind resourceKind) {
071        try {
072          switch (resourceKind) {
073            case LIFECYCLE:
074              if ("login".equals(resourceId) || "logout".equals(resourceId)) {
075                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
076                long timestamp = Long.MIN_VALUE;
077                for (File path : dirs) {
078                  File f = path.child(resourceId + ".groovy");
079                  if (f != null) {
080                    Resource sub = f.getResource();
081                    if (sub != null) {
082                      buffer.write(sub.getContent());
083                      buffer.write('\n');
084                      timestamp = Math.max(timestamp, sub.getTimestamp());
085                    }
086                  }
087                }
088                return Collections.singleton(new Resource(resourceId + ".groovy", buffer.toByteArray(), timestamp));
089              }
090              break;
091            case COMMAND:
092              // Find the resource first, we find for the first found
093              for (File path : dirs) {
094                File f = path.child(resourceId);
095                if (f != null) {
096                  return Collections.singleton(f.getResource());
097                }
098              }
099              break;
100            case CONFIG:
101              String path = "/" + resourceId;
102              File file = confFS.get(Path.get(path));
103              if (file != null) {
104                return Collections.singleton(loadConf(file));
105              }
106          }
107        } catch (IOException e) {
108          log.log(Level.WARNING, "Could not obtain resource " + resourceId, e);
109        }
110        return Collections.emptyList();
111      }
112    
113      /**
114       * List the resources id for a specific resource kind.
115       *
116       * @param kind the resource kind
117       * @return the resource ids
118       */
119      Iterable<String> listResourceId(ResourceKind kind) {
120        switch (kind) {
121          case COMMAND:
122            SortedSet<String> all = new TreeSet<String>();
123            try {
124              for (File path : dirs) {
125                for (File file : path.children()) {
126                  String fileName = file.getName();
127                  Matcher matcher = p.matcher(fileName);
128                  if (matcher.matches()) {
129                    String name = matcher.group(1);
130                    if (!"login".equals(name) && !"logout".equals(name)) {
131                      all.add(fileName);
132                    }
133                  }
134                }
135              }
136            }
137            catch (IOException e) {
138              e.printStackTrace();
139            }
140            return all;
141          default:
142            return Collections.emptyList();
143        }
144      }
145    
146      /**
147       * Refresh the fs system view. This is normally triggered by the periodic job but it can be manually
148       * invoked to trigger explicit refreshes.
149       */
150      void refresh() {
151        try {
152          File commands = cmdFS.get(Path.get("/"));
153          List<File> newDirs = new ArrayList<File>();
154          newDirs.add(commands);
155          for (File path : commands.children()) {
156            if (path.children().iterator().hasNext()) {
157              newDirs.add(path);
158            }
159          }
160          dirs = newDirs;
161        }
162        catch (IOException e) {
163          e.printStackTrace();
164        }
165      }
166    
167      /** . */
168      private static final byte[] SEPARATOR = System.getProperty("line.separator").getBytes();
169    
170      public static Resource loadConf(File file) throws IOException {
171        // Special handling for property files
172        if (file.getName().endsWith(".properties")) {
173          Iterator<Resource> i = file.getResources().iterator();
174          if (i.hasNext()) {
175            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
176            long timestamp = 0;
177            while (i.hasNext()) {
178              Resource resource = i.next();
179              byte[] bytes = resource.getContent();
180              buffer.write(bytes);
181              timestamp = Math.max(timestamp, resource.getTimestamp());
182              if (i.hasNext()) {
183                // Go to line
184                buffer.write(SEPARATOR);
185                // Cosmetic blank line
186                buffer.write(SEPARATOR);
187              }
188            }
189            return new Resource(file.getName(), buffer.toByteArray(), timestamp);
190          } else {
191            return null;
192          }
193        } else {
194          return file.getResource();
195        }
196      }
197    }