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
020package org.crsh.plugin;
021
022import org.crsh.vfs.FS;
023import org.crsh.vfs.File;
024import org.crsh.vfs.Path;
025import org.crsh.vfs.Resource;
026
027import java.io.ByteArrayOutputStream;
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.Collections;
031import java.util.Iterator;
032import java.util.List;
033import java.util.SortedSet;
034import java.util.TreeSet;
035import java.util.logging.Level;
036import java.util.logging.Logger;
037import java.util.regex.Matcher;
038import java.util.regex.Pattern;
039
040/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
041public 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}