View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  
18  /*
19   * Project: KernelConfigurator
20   * Created on 25-feb-2004
21   *
22   * Copyright (c)2003 Grid Systems
23   */
24  package com.gridsystems.config.modules.jvm;
25  
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.FileOutputStream;
29  import java.io.FileWriter;
30  import java.io.IOException;
31  import java.io.PrintWriter;
32  import java.text.MessageFormat;
33  import java.util.Collection;
34  import java.util.HashSet;
35  import java.util.Iterator;
36  import java.util.MissingResourceException;
37  import java.util.Properties;
38  import java.util.ResourceBundle;
39  
40  import com.gridsystems.config.ConfiguratorModel;
41  
42  /**
43   * Data Model for the JVM Configurator.
44   *
45   * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
46   * @version 1.0
47   */
48  public class JVMConfigModel implements ConfiguratorModel {
49  
50    /**
51     * Default max heap size.
52     */
53    public static final String DEFAULT_HEAP_MAX = "256";
54  
55    /**
56     * Default min heap size.
57     */
58    public static final String DEFAULT_HEAP_MIN = "128";
59  
60    /**
61     * Default thread stack size.
62     */
63    public static final String DEFAULT_STACK_SIZE = "";
64  
65    /**
66     * Default proxy usage mode.
67     */
68    public static final String DEFAULT_USE_PROXY = "false";
69  
70    /**
71     * Default proxy host name or IP address.
72     */
73    public static final String DEFAULT_PROXY_HOST = "";
74  
75    /**
76     * Default proxy port.
77     */
78    public static final String DEFAULT_PROXY_PORT = "";
79  
80    /**
81     * Default remote debug mode.
82     */
83    public static final String DEFAULT_DEBUG_MODE = "false";
84  
85    /**
86     * Default remote debug port.
87     */
88    public static final String DEFAULT_DEBUG_PORT = "8000";
89  
90    /**
91     * Default initial suspension flag in debug mode.
92     */
93    public static final String DEFAULT_DEBUG_SUSPEND = "true";
94  
95    /**
96     * Path to the configuration file.
97     */
98    public static final String DEFAULT_CONFIG_PATH = "../conf/jvm.properties";
99  
100   /**
101    * Model fields directly editable through the configurator UI.
102    */
103   private Properties values;
104 
105   /**
106    * System properties to add to the command-line.
107    */
108   private Properties props;
109 
110   /**
111    * Extra JVM flags to set in the command-line, different from system properties or
112    * those covered by values.
113    */
114   private HashSet<String> extraFlags;
115 
116   /**
117    * JVM command-line flags to set from values.
118    */
119   private HashSet<String> flags;
120 
121   /**
122    * Path to config File.
123    */
124   private final String configFilePath;
125 
126   /**
127    * Creates a new instance.
128    * @param configFilePath Path to file where read properties.
129    */
130   public JVMConfigModel(String configFilePath) {
131     this.configFilePath = configFilePath;
132     values = new Properties();
133     props = new Properties();
134     extraFlags = new HashSet<String>();
135   }
136 
137   /**
138    * Creates a new instance.
139    *
140    * @param values     The field values.
141    * @param props      The system properties
142    * @param extraFlags The extra flags set
143    */
144   /*
145   public JVMConfigModel(Properties values, Properties props, HashSet<String> extraFlags) {
146     this.values = values;
147     this.props = props;
148     this.extraFlags = extraFlags;
149   }
150   */
151 
152   /**
153    * Sets a system property.
154    *
155    * @param key   The system property name
156    * @param value The system property value
157    */
158   public void setProperty(String key, String value) {
159     this.props.setProperty(key, value);
160   }
161 
162   /**
163    * Sets a field value.
164    *
165    * @param key   The field name
166    * @param value The field value
167    */
168   public void setValue(String key, Object value) {
169     this.values.setProperty(key, value.toString());
170   }
171 
172   /**
173    * Gets a field value.
174    *
175    * @param key The field name
176    * @param def The default field value
177    * @return The field value
178    */
179   public String getValue(String key, String def) {
180     return this.values.getProperty(key, def);
181   }
182 
183   /**
184    * Gets a system property.
185    *
186    * @param key The system property name
187    * @param def The system property default value
188    * @return The system property value
189    */
190   public String getProperty(String key, String def) {
191     return this.props.getProperty(key, def);
192   }
193 
194   /**
195    * Adds a collection of command-line flags to the current set.
196    *
197    * @param flags The flags collection
198    */
199   public void addFlags(Collection<String> flags) {
200     this.extraFlags.addAll(flags);
201   }
202 
203   /**
204    * Gets the JVM heap minimum size.
205    *
206    * @return The JVM heap minimum size
207    */
208   public String getMinHeap() {
209     return this.getValue("heap.min", DEFAULT_HEAP_MIN);
210   }
211 
212   /**
213    * Gets the JVM heap maximum size.
214    *
215    * @return The JVM heap maximum size
216    */
217   public String getMaxHeap() {
218     return this.getValue("heap.max", DEFAULT_HEAP_MAX);
219   }
220 
221   /**
222    * Gets the JVM thread-stack size.
223    *
224    * @return The JVM thread-stack size
225    */
226   public String getStackSize() {
227     return this.getValue("stack.size", DEFAULT_STACK_SIZE);
228   }
229 
230   /**
231    * Gets whether proxy use is enabled or not.
232    *
233    * @return "true" if enabled; "false" if not
234    */
235   public String getUseProxy() {
236     return this.getValue("proxy.use", DEFAULT_USE_PROXY);
237   }
238 
239   /**
240    * Gets the proxy host name / IP.
241    *
242    * @return The proxy host name or IP address
243    */
244   public String getProxyHost() {
245     return this.getValue("proxy.host", DEFAULT_PROXY_HOST);
246   }
247 
248   /**
249    * Gets the proxy port.
250    *
251    * @return The proxy port
252    */
253   public String getProxyPort() {
254     return this.getValue("proxy.port", DEFAULT_PROXY_PORT);
255   }
256 
257   /**
258    * Gets whether debug mode is enabled or not.
259    *
260    * @return "true" if remote debugging is enabled; "false" if not
261    */
262   public String getDebugEnabled() {
263     return this.getValue("debug.enable", DEFAULT_DEBUG_MODE);
264   }
265 
266   /**
267    * Gets the remote debug port.
268    *
269    * @return The debug port the debugger will connect to
270    */
271   public String getDebugPort() {
272     return this.getValue("debug.port", DEFAULT_DEBUG_PORT);
273   }
274 
275   /**
276    * Gets whether the server JVM will start suspended.
277    *
278    * @return "true" if the JVM will start suspended; "false" if not
279    */
280   public String getDebugSuspend() {
281     return this.getValue("debug.suspend", DEFAULT_DEBUG_SUSPEND);
282   }
283 
284   /**
285    * Copies the specified model data into this instance. Only "editable" fields are
286    * copied. The extraFlags set is not included, as it is not directly editable from
287    * this configurator views.
288    *
289    * @param src The source model
290    */
291   public void copyModel(JVMConfigModel src) {
292     this.values = (Properties)src.values.clone();
293     this.props  = (Properties)src.props.clone();
294   }
295 
296   /**
297    * @see com.gridsystems.config.ConfiguratorModel#load()
298    */
299   public void load() {
300     File f = new File(this.configFilePath);
301     if (f.exists()) {
302       FileInputStream fis = null;
303       try {
304         fis = new FileInputStream(f);
305         Properties p = new Properties();
306         p.load(fis);
307 
308         this.props = extractProperties(p, "system.");
309         this.extraFlags = extractSet(p, "flags.");
310         this.values = p;
311       } catch (Exception e) {
312         e.printStackTrace();
313       } finally {
314         try { fis.close(); } catch (Exception ignore) { }
315       }
316     }
317   }
318 
319   /**
320    * {@inheritDoc}
321    */
322   public void store() throws IOException {
323     File f = new File(this.configFilePath);
324     f.getParentFile().mkdir();
325     Properties p = new Properties();
326     embed(p, "", values);
327     embed(p, "system.", props);
328     embed(p, "flags.", extraFlags);
329 
330     FileOutputStream fos = null;
331     try {
332       fos = new FileOutputStream(f);
333       p.store(fos, "JVM Properties");
334     } catch (Exception e) {
335       e.printStackTrace();
336     } finally {
337       try { fos.close(); } catch (Exception ignore) { }
338     }
339   }
340 
341   /**
342    * {@inheritDoc}
343    */
344   public void apply() throws IOException {
345     buildFlags();
346     writeBat();
347     writeSh();
348   }
349 
350   /**
351    * Writes the DOS shell script for setting the correct JVM options.
352    *
353    * @throws IOException If an error occurs
354    */
355   private void writeBat() throws IOException {
356     File f = new File("setenv.bat");
357     PrintWriter out = null;
358     try {
359       out = new PrintWriter(new FileWriter(f));
360       out.println("@echo off");
361       out.println("set JAVA_OPTS=" + getJavaOpts());
362     } finally {
363       if (out != null) {
364         out.close();
365       }
366     }
367   }
368 
369   /**
370    * Writes the Unix shell script for setting the correct JVM options.
371    *
372    * @throws IOException If an error occurs
373    */
374   private void writeSh() throws IOException {
375     File f = new File("setenv.sh");
376     PrintWriter out = null;
377     try {
378       out = new PrintWriter(new FileWriter(f));
379       out.println("#!/bin/sh");
380       out.println("JAVA_OPTS=\"" + getJavaOpts() + "\"");
381       out.println("export JAVA_OPTS");
382     } finally {
383       if (out != null) {
384         out.close();
385       }
386     }
387   }
388 
389   /**
390    * Builds the JVM command-line flags.
391    *
392    * @return The command-line flags
393    */
394   private String getJavaOpts() {
395     StringBuffer sb = new StringBuffer();
396     boolean first = true;
397     for (Iterator it = flags.iterator(); it.hasNext();) {
398       String flag = (String)it.next();
399       if (first) {
400         first = false;
401       } else {
402         sb.append(' ');
403       }
404       sb.append(flag);
405     }
406     return sb.toString();
407   }
408 
409   /**
410    * Builds the contents of the flags set, from data in the values set.
411    */
412   private void buildFlags() {
413     flags = new HashSet<String>();
414     flags.addAll(extraFlags);
415 
416     for (Iterator it = props.keySet().iterator(); it.hasNext();) {
417       String key = (String)it.next();
418       String value = props.getProperty(key);
419       // values containing blank spaces must be surrounded with "
420       if (value.indexOf(" ") >= 0) {
421         flags.add("-D" + key + "=\"" + value + "\"");
422       } else {
423         flags.add("-D" + key + "=" + value);
424       }
425     }
426 
427     String value = formatValue("heap.min", "-Xms{0}M");
428     if (value != null) {
429       flags.add(value);
430     }
431 
432     value = formatValue("heap.max", "-Xmx{0}M");
433     if (value != null) {
434       flags.add(value);
435     }
436 
437     value = formatValue("stack.size", "-Xss{0}M");
438     if (value != null) {
439       flags.add(value);
440     }
441 
442     value = values.getProperty("debug.enable");
443     if ("true".equals(value)) {
444       flags.add("-Xdebug");
445       String port = values.getProperty("debug.port");
446       boolean suspend = "true".equals(values.getProperty("debug.suspend"));
447       flags.add("-Xrunjdwp:transport=dt_socket,address=" + port
448                 + ",server=y,suspend=" + (suspend ? "y" : "n"));
449       flags.add("-Djpda.port=" + port);
450     }
451 
452     value = values.getProperty("proxy.use");
453     if ("true".equals(value)) {
454       flags.add("-DproxySet=true");
455       flags.add("-DproxyHost=" + values.getProperty("proxy.host"));
456       flags.add("-DproxyPort=" + values.getProperty("proxy.port"));
457     }
458   }
459 
460   /**
461    * Formats the field value with the specified name using the specified pattern.
462    *
463    * @param key    The field name
464    * @param format The formatting pattern
465    * @return The formatted string
466    */
467   private String formatValue(String key, String format) {
468     String value = values.getProperty(key);
469     if (value == null) {
470       return null;
471     }
472     value = value.trim();
473     if (value.equals("")) {
474       return null;
475     }
476 
477     return MessageFormat.format(format, new Object[] { value });
478   }
479 
480   /**
481    * Adds all items in src to dest, adding the specified prefix to the keys.
482    *
483    * @param dest   The destination Properties instance
484    * @param prefix The prefix to set
485    * @param src    The source Properties instance
486    */
487   private void embed(Properties dest, String prefix, Properties src) {
488     for (Iterator it = src.keySet().iterator(); it.hasNext();) {
489       String key = (String)it.next();
490       String value = src.getProperty(key);
491       dest.setProperty(prefix + key, value);
492     }
493   }
494 
495   /**
496    * Adds all items in src to dest, adding the specified prefix to the keys.
497    *
498    * @param dest   The destination Properties instance
499    * @param prefix The prefix to set
500    * @param src    The source set
501    */
502   private void embed(Properties dest, String prefix, HashSet src) {
503     int index = 0;
504     for (Iterator it = src.iterator(); it.hasNext();) {
505       String item = (String)it.next();
506       dest.setProperty(prefix + "item" + index, item);
507       index += 1;
508     }
509   }
510 
511   /**
512    * Gets a Properties instance containing the values previously embedded in src with the
513    * specified prefix.
514    *
515    * @param src    The source Properties instance
516    * @param prefix The embedding prefix
517    * @return A Properties instance containing the previously embedded items
518    */
519   private Properties extractProperties(Properties src, String prefix) {
520     Properties dest = new Properties();
521 
522     for (Iterator it = src.keySet().iterator(); it.hasNext();) {
523       String key = (String)it.next();
524       if (key.startsWith(prefix)) {
525         String value = src.getProperty(key);
526         String destKey = key.substring(prefix.length());
527         dest.setProperty(destKey, value);
528         it.remove();
529       }
530     }
531 
532     return dest;
533   }
534 
535   /**
536    * Gets a HashSet instance containing the values previously embedded in src with the
537    * specified prefix.
538    *
539    * @param src    The source Properties instance
540    * @param prefix The embedding prefix
541    * @return A HashSet instance containing the previously embedded items
542    */
543   private HashSet<String> extractSet(Properties src, String prefix) {
544     HashSet<String> dest = new HashSet<String>();
545 
546     for (Iterator it = src.keySet().iterator(); it.hasNext();) {
547       String key = (String)it.next();
548       if (key.startsWith(prefix)) {
549         String value = src.getProperty(key);
550         dest.add(value);
551         it.remove();
552       }
553     }
554 
555     return dest;
556   }
557 
558   /**
559    * Validate data of this Model.
560    *
561    * @throws IOException In case of invalid data
562    */
563   public void validate() throws IOException {
564     IOException ioe = null;
565 
566     int minValue;
567     try {
568       minValue = getMemoryValue(getMinHeap());
569     } catch (Exception e) {
570       Object[] params = new Object[] {getMinHeap()};
571       ioe = new IOException(getString("errors.restoreHeadMin", params));
572       minValue = Integer.parseInt(DEFAULT_HEAP_MIN);
573       setValue("heap.min", DEFAULT_HEAP_MIN);
574     }
575     int maxValue;
576     try {
577       maxValue = getMemoryValue(getMaxHeap());
578     } catch (Exception e) {
579       Object[] params = new Object[] {getMinHeap()};
580       ioe = new IOException(getString("errors.restoreHeadMax", params));
581       maxValue = Integer.parseInt(DEFAULT_HEAP_MAX);
582       setValue("heap.max", DEFAULT_HEAP_MAX);
583     }
584     //Check memory values
585     if (minValue > maxValue) {
586       if (ioe == null) {
587         ioe = new IOException(getString("errors.minGreaterMax"));
588       }
589     }
590     if (ioe != null) {
591       throw ioe;
592     }
593   }
594 
595   /**
596    * Gets a localized string.
597    *
598    * @param key The i18n key
599    * @return    The localized string, or null if none found for key
600    * @throws NullPointerException if the bundle is not set
601    */
602   public String getString(String key) {
603     ResourceBundle bundle = JVMConfigurator.getInstance().getBundle();
604     if (bundle == null) {
605       throw new NullPointerException("Bundle not set");
606     }
607 
608     try {
609       return bundle.getString(key);
610     } catch (MissingResourceException e) {
611       return null;
612     }
613   }
614 
615   /**
616    * Gets a localized string, using the found string as a pattern to be used
617    * by a MessageFormat instance.
618    *
619    * @param key    The key for the localized pattern
620    * @param params The parameters for pattern substitution
621    * @return       The localized / formatter string
622    * @throws NullPointerException if the bundle is not set
623    */
624   public String getString(String key, Object[] params) {
625     ResourceBundle bundle = JVMConfigurator.getInstance().getBundle();
626     if (bundle == null) {
627       throw new NullPointerException("Bundle not set");
628     }
629 
630     try {
631       String pattern = bundle.getString(key);
632       return MessageFormat.format(pattern, params);
633     } catch (MissingResourceException e) {
634       return key + "#not found";
635     }
636   }
637 
638   /**
639    * It converts the string memory to its integer bytes memory
640    * value.
641    *
642    * @param memory Text memory value.
643    * @return Number of bytes of the memory value.
644    */
645   private int getMemoryValue(String memory) {
646     if (memory == null || memory.equals("")) {
647       //Empty memory
648       return 0;
649     }
650     //  MegaBytes value
651     return Integer.parseInt(memory);
652   }
653 }