View Javadoc

1   // 
2   // Copyright (c) 2003-2006, Caltha - Gajda, Krzewski, Mach, Potempski Sp.J. 
3   // All rights reserved. 
4   //   
5   // Redistribution and use in source and binary forms, with or without modification,  
6   // are permitted provided that the following conditions are met: 
7   //   
8   // * Redistributions of source code must retain the above copyright notice,  
9   // this list of conditions and the following disclaimer. 
10  // * Redistributions in binary form must reproduce the above copyright notice,  
11  // this list of conditions and the following disclaimer in the documentation  
12  // and/or other materials provided with the distribution. 
13  // * Neither the name of the Caltha - Gajda, Krzewski, Mach, Potempski Sp.J.  
14  // nor the names of its contributors may be used to endorse or promote products  
15  // derived from this software without specific prior written permission. 
16  // 
17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
18  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
19  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
20  // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,  
21  // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  
22  // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
23  // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
24  // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
25  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
26  // POSSIBILITY OF SUCH DAMAGE. 
27  //
28  
29  package org.objectledge.external;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.util.Set;
34  import java.util.regex.Pattern;
35  
36  import org.jcontainer.dna.Configuration;
37  import org.jcontainer.dna.ConfigurationException;
38  import org.objectledge.ComponentInitializationError;
39  import org.objectledge.filesystem.FileSystem;
40  import org.objectledge.filesystem.LocalFileSystemProvider;
41  import org.objectledge.utils.StringUtils;
42  
43  /***
44   * A helper object for manangin a directory containing executable scripts in the local filesystem.
45   * <p>
46   * The helper has two functions:
47   * <ul>
48   * <li>Ensuring that the scripts in the local filesystem have apropriate privileges. This is useful
49   * after the scripts get extracted from war/jar files using the
50   * {@link org.objectledge.filesystem.ContentExtractor}</li>
51   * <li>Providing absolute paths to the script files in the server filesystem. This is necessary to
52   * execute the scritpts.</li>
53   * </ul>
54   * 
55   * @author <a href="rafal@caltha.pl">RafaƂ Krzewski</a>
56   * @version $Id: ScriptDirectory.java,v 1.2 2006/03/28 18:02:38 zwierzem Exp $
57   */
58  public class ScriptDirectory
59  {
60      public static final String DEFAULT_PROVIDER = "local";
61  
62      public static final String DEFAULT_DIRECTORY = null;
63  
64      public static final String DEFAULT_PATH_PATTERN = ".*//.(sh|pl|py)$";
65  
66      public static final String DEFAULT_COMMAND = "chmod a+x";
67  
68      private static String fs = System.getProperty("file.separator");
69  
70      private final LocalFileSystemProvider provider;
71  
72      private final String directory;
73  
74      private final File baseDir;
75  
76      /***
77       * Creates a new ScriptDirectory instance.
78       * 
79       * @param fileSystem the FileSystem component.
80       * @param providerName the name of the provider to use ("local" in normal circumstances).
81       * @param directory the directory to scan for scripts in order to ensure executability (null to
82       *        disable).
83       * @param pathPattern pattern to match file name against in order to decide if they should be
84       *        made executable.
85       * @param command the command string to execute to make a script executable - path is appended
86       *        as additional argument argument.
87       */
88      public ScriptDirectory(FileSystem fileSystem, String providerName, String directory,
89          String pathPattern, String command)
90      {
91          this.directory = directory;
92          this.provider = (LocalFileSystemProvider)fileSystem.getProvider(providerName);
93          this.baseDir = null;
94          ensureExecutable(pathPattern, command);
95      }
96  
97      /***
98       * Creates a new ScriptDirectory instance.
99       * 
100      * @param baseDir the script directory in the host filesystem.
101      * @param pathPattern pattern to match file name against in order to decide if they should be
102      *        made executable.
103      * @param command the command string to execute to make a script executable - path is appended
104      *        as additional argument argument.
105      */
106     public ScriptDirectory(File baseDir, String pathPattern, String command)
107     {
108         this.directory = null;
109         this.provider = null;
110         this.baseDir = baseDir;
111         ensureExecutable(pathPattern, command);
112     }
113 
114     /***
115      * Creates a new ScriptDirectory instance.
116      * 
117      * @param fileSystem the FileSystem component.
118      * @param configuration component configuration.
119      * @throws ConfigurationException
120      */
121     public ScriptDirectory(FileSystem fileSystem, Configuration configuration)
122         throws ConfigurationException
123     {
124         if(configuration.getChild("ledge-fs", false) != null)
125         {
126             String providerName = configuration.getChild("ledge-fs").getChild("provider").getValue(
127                 DEFAULT_PROVIDER);
128             this.provider = (LocalFileSystemProvider)fileSystem.getProvider(providerName);
129             this.directory = configuration.getChild("ledge-fs").getChild("directory").getValue();
130             this.baseDir = null;
131         }
132         else
133         {
134             this.provider = null;
135             this.directory = null;
136             this.baseDir = new File(configuration.getChild("host-fs").getChild("directory")
137                 .getValue());
138         }
139         String pathPattern = configuration.getChild("ensure-executable").getChild("pattern")
140             .getValue(DEFAULT_PATH_PATTERN);
141         String command = configuration.getChild("ensure-executable").getChild("command").getValue(
142             DEFAULT_COMMAND);
143         ensureExecutable(pathPattern, command);
144     }
145 
146     /***
147      * Returns the canonical script path in the server's filesystem.
148      * 
149      * @param script the path inside Ledge virtual filesystem.
150      * @return the canonical script path in the server's filesystem.
151      * @throws IOException if the path cannot be determined.
152      */
153     public String getPath(String script)
154         throws IOException
155     {
156         if(provider != null)
157         {
158             return provider.getFile(directory + "/" + script).getCanonicalPath();
159         }
160         else
161         {
162             return new File(baseDir, script.replace("/", fs)).getCanonicalPath();
163         }
164     }
165 
166     // implementation ///////////////////////////////////////////////////////////////////////////
167 
168     private void ensureExecutable(String pathPattern, String command)
169     {
170         if(pathPattern != null && pathPattern.length() > 0)
171         {
172             try
173             {
174                 if(provider != null)
175                 {
176                     ensureExecutable(provider, directory, Pattern.compile(pathPattern), command
177                         .split(" "));
178                 }
179                 else
180                 {
181                     ensureExecutable(baseDir, Pattern.compile(pathPattern), command.split(" "));
182                 }
183             }
184             catch(Exception e)
185             {
186                 throw new ComponentInitializationError(
187                     "failed to ensure script execution permissions", e);
188             }
189         }
190     }
191 
192     private static void ensureExecutable(LocalFileSystemProvider provider, String directory,
193         Pattern namePattern, String[] commandTokens)
194         throws IOException, InterruptedException
195     {
196         Set<String> items = provider.list(directory);
197         for(String item : items)
198         {
199             String path = directory + "/" + item;
200             if(provider.isDirectory(path))
201             {
202                 ensureExecutable(provider, path, namePattern, commandTokens);
203             }
204             else if(namePattern.matcher(path).matches())
205             {
206                 Runtime.getRuntime().exec(
207                     StringUtils.push(commandTokens, provider.getFile(path).getCanonicalPath()))
208                     .waitFor();
209             }
210         }
211     }
212 
213     private static void ensureExecutable(File dir, Pattern namePattern, String[] commandTokens)
214         throws InterruptedException, IOException
215     {
216         File[] items = dir.listFiles();
217         for(File item : items)
218         {
219             if(item.isDirectory())
220             {
221                 ensureExecutable(item, namePattern, commandTokens);
222             }
223             else if(namePattern.matcher(item.getPath()).matches())
224             {
225                 Runtime.getRuntime().exec(StringUtils.push(commandTokens, item.getCanonicalPath()))
226                     .waitFor();
227             }
228         }
229     }
230 }