View Javadoc

1   /*
2    * $Id $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.modules.boot;
12  
13  import java.io.BufferedReader;
14  import java.io.File;
15  import java.io.FileInputStream;
16  import java.io.FileOutputStream;
17  import java.io.FileReader;
18  import java.io.IOException;
19  import java.io.InputStreamReader;
20  import java.nio.channels.FileChannel;
21  import java.util.jar.JarEntry;
22  import java.util.jar.JarFile;
23  import java.util.jar.JarOutputStream;
24  import java.util.zip.ZipEntry;
25  
26  /**
27   * This class has methods for displaying the EULA and saving the license acceptance
28   * acknowledgment.
29   */
30  public class LicenseHandler
31  {
32      private File muleHome;
33      private File muleBase;
34      private static int maxRowsToDisplay = 80;
35  
36      public static String defaultLicenseType = "MuleSource Public License";
37      public static String defaultLicenseVersion = "UNKNOWN";
38      public static String ackJarName = "mule-local-install.jar";
39      public static String ackLicenseName = "META-INF/mule/LICENSE.txt";
40      public static String ackFileName = "META-INF/mule/license.props";
41      public static String licenseFileName = "LICENSE.txt";
42  
43      /**
44       * Constructor for when we only know MULE_HOME This is the constructor that the
45       * GUI installer should use.
46       * 
47       * @param muleHome File pointing to MULE_HOME
48       */
49      public LicenseHandler(File muleHome)
50      {
51          this.muleHome = muleHome;
52          this.muleBase = muleHome;
53      }
54  
55      /**
56       * Constructor for when we know both MULE_HOME and MULE_BASE This is the
57       * constructor used by MuleBootstrap
58       * 
59       * @param muleHome File pointing to MULE_HOME
60       * @param muleBase File pointing to MULE_BASE
61       */
62      public LicenseHandler(File muleHome, File muleBase)
63      {
64          this.muleHome = muleHome;
65          this.muleBase = muleBase;
66      }
67  
68      /**
69       * Display the EULA and get the user's acceptance. Note that only a missing
70       * license file or a non-yes answer will cause this to return false. If the user
71       * accepts, but we can't write the license ack file for some reason, we'll still
72       * return true.
73       * 
74       * @return boolean whether the license was accepted or not
75       */
76      public boolean getAcceptance()
77      {
78          String licenseType = "";
79          String licenseVersion = "";
80  
81          try
82          {
83              File licenseFile = new File(muleHome, licenseFileName);
84              File muleLib = new File(muleHome, "lib/mule");
85  
86              // Make sure the license file exists
87              // Also make sure that MULE_HOME/lib/mule exists - it
88              // should, of course, but it doesn't hurt to check since
89              // we'll need that directory later
90  
91              if (!licenseFile.exists() || !muleLib.exists())
92              {
93                  System.out
94                      .println("\nYour Mule installation seems to be incomplete. Please try downloading it again from http://mule.mulesource.org/display/MULE/Download and start again.");
95                  return false;
96              }
97  
98              System.out.println("\n\nPlease read over the following license agreement carefully:\n\n");
99  
100             int row = 1;
101             String input = "";
102 
103             BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
104             BufferedReader br = new BufferedReader(new FileReader(licenseFile));
105 
106             while (br.ready())
107             {
108                 String line = br.readLine();
109 
110                 if (row == 1) licenseType = line;
111                 if (row == 2 && line.startsWith("Version ")) licenseVersion = line.substring(8);
112 
113                 if ((row % maxRowsToDisplay) == 0)
114                 {
115                     System.out.print("\nHit return to continue ... ");
116                     input = is.readLine();
117                 }
118 
119                 System.out.println(line);
120                 row++;
121             }
122 
123             System.out.print("\n\nDo you accept the terms and conditions of this license agreement [y/n]?");
124             input = is.readLine();
125 
126             if (!input.toLowerCase().startsWith("y"))
127             {
128                 System.out
129                     .println("\nSorry, until you accept the terms and conditions of this EULA, you won't be able to start Mule");
130                 return false;
131             }
132         }
133         catch (Exception e)
134         {
135             System.out
136                 .println("\nSorry, we encountered an error in processing your license agreement - please try again");
137             return false;
138         }
139 
140         if (licenseType.equals("")) licenseType = defaultLicenseType;
141         if (licenseVersion.equals("")) licenseVersion = defaultLicenseVersion;
142 
143         try
144         {
145             saveLicenseAck(licenseType, licenseVersion);
146         }
147         catch (Exception e)
148         {
149             System.out.println(e);
150             e.printStackTrace();
151         }
152 
153         return true;
154     }
155 
156     /**
157      * Saves the license acceptance acknowledgement file. This method should be used
158      * by the GUI installer.
159      * <p>
160      * For now, the acknowlegment file is license.props and contains:
161      * <ul>
162      * <li>LicenseVersion:
163      * <li>LicenseAcceptanceDate:
164      * <li>LicenseType:
165      * </ul>
166      * We are also making a copy of the license file into the jar as well. The logic
167      * here is:
168      * <ol>
169      * <li> Normally, save the license ack in a jar in MULE_HOME/lib/mule called
170      * "mule-local-install.jar"
171      * <li> If MULE_HOME/lib/mule is not writable AND MULE_BASE != MULE_HOME, try to
172      * save in MULE_BASE/lib/user
173      * <li> If MULE_BASE/lib/user is not writable, something is probably strange and
174      * we need a third option ... which is ... well, I'm sure there is one ...
175      * </ol>
176      * 
177      * @param licenseType type of license - for now, should be just MuleSource Public
178      *            License
179      * @param licenseVersion version of license - for now, should be 1.1.3
180      * @throws Exception if there is nowhere to write the file or somehow the jar
181      *             creation fails (disk full, etc.)
182      */
183     public void saveLicenseAck(String licenseType, String licenseVersion) throws Exception
184     {
185         File muleLib = new File(muleHome, "lib/mule");
186         File tempJar = createAckJarFile(licenseType, licenseVersion);
187 
188         // First check if the file exists. We do this in case the license.props
189         // has not yet been loaded into the classpath - which might be the case
190         // with the GUI installer.
191         if (licenseJarExists())
192         {
193             return;
194         }
195 
196         if (!muleLib.canWrite())
197         {
198             // If we can't write to MULE_HOME/lib/mule, try MULE_BASE/lib/user
199             if (!muleHome.getCanonicalFile().equals(muleBase.getCanonicalFile()))
200             {
201                 muleLib = new File(muleBase, "lib/user");
202 
203                 if (!muleLib.canWrite())
204                     throw new Exception("No write permissions for " + ackJarName
205                                     + " in either MULE_HOME or MULE_BASE");
206 
207             }
208             else
209             {
210                 throw new Exception("No write permission for " + ackJarName);
211             }
212         }
213 
214         // Now we have a directory to create the jar to, so let's rename
215         // the temporary one
216         File newJarFile = new File(muleLib, ackJarName);
217 
218         if (newJarFile.exists())
219             throw new Exception("Unable to rename temporary jar to " + newJarFile.getAbsolutePath()
220                             + " a file with this name already exists!");
221 
222         if (!tempJar.renameTo(newJarFile))
223         {
224             // Make sure the file is still there - sometimes renaming
225             // claims to have faild when it actually works
226 
227             if (!tempJar.exists()) return;
228 
229             FileChannel srcChannel = null;
230             FileChannel destChannel = null;
231 
232             try
233             {
234                 srcChannel = new FileInputStream(tempJar.getAbsolutePath()).getChannel();
235                 destChannel = new FileOutputStream(newJarFile).getChannel();
236                 destChannel.transferFrom(srcChannel, 0, srcChannel.size());
237             }
238             catch (Exception e)
239             {
240                 throw new Exception("Unable to rename temporary jar to " + newJarFile.getAbsolutePath(), e);
241             }
242             finally
243             {
244                 try { if (srcChannel != null) srcChannel.close(); } catch (Exception e1) { }
245                 try { if (destChannel != null) destChannel.close(); } catch (Exception e2) { }
246                 try { tempJar.delete(); } catch (Exception e3) { }
247             }
248         }
249 
250         if (!newJarFile.exists())
251             throw new Exception("Unable to rename temporary jar to " + newJarFile.getAbsolutePath());
252     }
253 
254     /**
255      * This method checks to see if there is a license jar file already. It checks
256      * both MULE_HOME/lib/mule and, if relevant, MULE_BASE/lib/user.
257      */
258 
259     public boolean licenseJarExists()
260     {
261         try
262         {
263             File muleLib = new File(muleHome, "lib/mule");
264             File testJarFile = new File(muleLib, ackJarName);
265             JarFile jar = null;
266 
267             if (testJarFile.exists())
268             {
269                 jar = new JarFile(testJarFile);
270             }
271             else
272             {
273                 // Not in MULE_HOME/lib/mule, if MULE_BASE is defined
274                 // (and therefore != MULE_HOME), check in MULE_BASE/lib/user
275                 if (!muleHome.getCanonicalFile().equals(muleBase.getCanonicalFile()))
276                 {
277                     muleLib = new File(muleBase, "lib/user");
278                     testJarFile = new File(muleLib, ackJarName);
279 
280                     if (testJarFile.exists())
281                     {
282                         jar = new JarFile(testJarFile);
283                     }
284                 }
285             }
286 
287             if (jar != null)
288             {
289                 ZipEntry entry = jar.getEntry(ackFileName);
290 
291                 // The only way this method will return true is if we
292                 // find the license.props file in the jar
293                 if (entry != null)
294                 {
295                     return true;
296                 }
297             }
298         }
299         catch (Exception e)
300         {
301             System.out.println("Unknown error checking for license jar: " + e.toString());
302         }
303 
304         return false;
305     }
306 
307     /**
308      * This method will create a temporary jar file with the license ack file. It
309      * will either return the File or throw an Exception
310      * 
311      * @throws Exception
312      */
313     private File createAckJarFile(String licenseType, String licenseVersion) throws Exception
314     {
315         File tempJar = File.createTempFile(ackJarName, null);
316         File licenseFile = new File(muleHome, licenseFileName);
317         JarOutputStream newJar;
318         FileOutputStream fos;
319 
320         String ackData = "LicenseType=" + licenseType + "\n";
321         ackData += "LicenseVersion=" + licenseVersion + "\n";
322         ackData += "LicenseDate=" + (new java.util.Date()).toString() + "\n";
323 
324         try
325         {
326             fos = new FileOutputStream(tempJar);
327             newJar = new JarOutputStream(fos);
328         }
329         catch (IOException ioe)
330         {
331             throw new Exception("Unable to create temporary jar file");
332         }
333 
334         byte buffer[] = new byte[1024];
335         int bytesRead;
336         FileInputStream fis = null;
337 
338         try
339         {
340             fis = new FileInputStream(licenseFile);
341 
342             JarEntry entry = new JarEntry(ackFileName);
343             newJar.putNextEntry(entry);
344             newJar.write(ackData.getBytes(), 0, ackData.getBytes().length);
345 
346             entry = new JarEntry(ackLicenseName);
347             newJar.putNextEntry(entry);
348 
349             while ((bytesRead = fis.read(buffer)) != -1)
350             {
351                 newJar.write(buffer, 0, bytesRead);
352             }
353 
354             // wait till the physical file is written and released by the OS
355             newJar.flush();
356             fos.getFD().sync();
357         }
358         catch (IOException ioe)
359         {
360             throw new Exception("Unable to write " + ackFileName + " to temporary jar file");
361         }
362         finally
363         {
364             if (fis != null)
365             {
366                 try
367                 {
368                     fis.close();
369                 }
370                 catch (Exception e)
371                 {
372                     // ignored
373                 }
374             }
375         }
376 
377         try
378         {
379             newJar.close();
380         }
381         catch (IOException ioe)
382         {
383             throw new Exception("Unable to close temporary jar file");
384         }
385 
386         return tempJar;
387     }
388 
389 }