View Javadoc

1   /*
2    * Copyright (c) 1998-2002 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  
19  package jgroup.arm;
20  
21  import static jgroup.util.log.ReplicaEvent.Type.Failed;
22  import static jgroup.util.log.ReplicaEvent.Type.Shutdown;
23  
24  import java.io.BufferedReader;
25  import java.io.InputStreamReader;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Modifier;
29  
30  import jgroup.core.ConfigManager;
31  import jgroup.core.GroupManager;
32  import jgroup.core.arm.ExecException;
33  import jgroup.core.arm.ExecService;
34  import jgroup.relacs.config.AppConfig;
35  import jgroup.util.log.Eventlogger;
36  import jgroup.util.log.ReplicaEvent;
37  
38  import org.apache.log4j.LogManager;
39  import org.apache.log4j.Logger;
40  
41  /**
42   *  The application starter class is wrapper for starting a server replica
43   *  in a separate child JVM process, and sets up a pipe between the parent
44   *  and child process.  This allows the parent process (the ExecDaemon) to
45   *  control the child process (the server replica), for example the ExecDaemon
46   *  may wish to kill the replica.  Note that, unless the server replica is
47   *  using the <code>Replication</code> service, or if the server has specific
48   *  shutdown needs, the server should consider adding a shutdown hook so as
49   *  to ensure that the replica releases any held resources when being killed.
50   *
51   *  This class can also be used to start a server through its constructor
52   *  without having to recompile it.  That is, this wrapper will take care of
53   *  obtaining the group manager for the server.  This means that the server
54   *  can be made into a replicated server without changing it.  This assumes
55   *  that the server does not need to use any of the services or listeners
56   *  provided through the Jgroup group manager.  For simple replication
57   *  this should be no problem.
58   *
59   *  @author Hein Meling
60   *  @since Jgroup 2.1
61   */
62  public final class ApplicationStarter
63  {
64  
65    ////////////////////////////////////////////////////////////////////////////////////////////
66    // Logger
67    ////////////////////////////////////////////////////////////////////////////////////////////
68  
69    /** Obtain logger for this class */
70    private static final Logger log = Logger.getLogger(ApplicationStarter.class);
71  
72  
73    ////////////////////////////////////////////////////////////////////////////////////////////
74    // Fields
75    ////////////////////////////////////////////////////////////////////////////////////////////
76  
77    /* The application object */
78    private Object obj;
79  
80    /* 
81     * The GroupManager of the application associated with 
82     * this ApplicationStarter object
83     */
84    private GroupManager gm;
85  
86  
87    ////////////////////////////////////////////////////////////////////////////////////////////
88    // Constructor
89    ////////////////////////////////////////////////////////////////////////////////////////////
90  
91    public ApplicationStarter(final String[] args)
92      throws ExecException, ClassNotFoundException
93    {
94      Class c = Class.forName(args[0]);
95      String mainError = args[0]
96        + " does not implement a 'public static void main(String[] args)' method.";
97  
98      final AppConfig app = AppConfig.getApplication(c);
99      /*
100      * Set up a shutdown hook for this ApplicationStarter object, to
101      * allow detection of replica crash in offline analysis.
102      */
103     Runtime.getRuntime().addShutdownHook(new Thread("AS-ShutdownHook") {
104       public void run() {
105         log.info("AS-ShutdownHook: shutdown for " + app);
106         if (Eventlogger.ENABLED) {
107           Eventlogger.logEventFlush(new ReplicaEvent(Failed, app.getGroupId()));
108         }
109     }});
110 
111     try {
112 
113       /*
114        * Check for 'main(String[] args)' method in the specified class.
115        */
116       Method main = c.getDeclaredMethod("main", new Class[] { String[].class });
117       int m = main.getModifiers();
118       if (!Modifier.isPublic(m) || !Modifier.isStatic(m)) {
119         throw new ExecException(mainError);
120       }
121       
122       /* Copy the argument part of the array */
123       String[] argv = new String[args.length-1];
124       if (argv.length > 0)
125         System.arraycopy(args, 1, argv, 0, args.length);
126 
127       /*
128        * Invoke the main method to start the replica in this JVM.
129        */
130       main.invoke(null, new Object[] { argv });
131 
132     } catch (InvocationTargetException e) {
133       log.warn(args[0] + " has thrown an exception", e.getCause());
134       throw new ExecException(args[0]
135         + " has thrown an exception during invocation of the main method.", e.getCause());
136     } catch (IllegalAccessException e) {
137       throw new ExecException(args[0] + " failed to start", e);
138     } catch (NoSuchMethodException e) {
139       throw new ExecException(mainError, e);
140     }
141 
142     /*
143      * This thread connects to its parent process to receive commands.
144      * That is, the execdaemon creates a replica through the ApplicationStarter
145      * and uses this command controller to shut down the replica once
146      * it should be removed.
147      */
148     Thread shutdownThrd = new Thread("AS-CommandParser") {
149       public void run() {
150         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
151         String cmd;
152         try {
153           while ((cmd = in.readLine()) != null) {
154             if (log.isDebugEnabled()) {
155               log.debug("COMMAND RECEIVED: " + cmd);
156             }
157             if (cmd.equals(ExecService.SHUTDOWN_REPLICA)) {
158               if (Eventlogger.ENABLED) {
159                 Eventlogger.logEventFlush(new ReplicaEvent(Shutdown, app.getGroupId()));
160               }
161               LogManager.shutdown();
162               System.out.println(new ReplicaEvent(Shutdown, app.getGroupId()));
163               Runtime.getRuntime().halt(0);
164             }
165           }
166           in.close();
167         } catch (java.io.IOException e) {
168           log.error("Replica failed reading command from ExecDaemon", e);
169         }
170       }
171     };
172     shutdownThrd.setPriority(Thread.NORM_PRIORITY+1);
173     shutdownThrd.setDaemon(true);
174     shutdownThrd.start();
175 
176     if (log.isDebugEnabled())
177       log.debug("Created replica: " + args[0]);
178   }
179 
180 
181   public ApplicationStarter(String className)
182     throws Exception
183   {
184     Class appClass = Class.forName(className);
185     AppConfig app = AppConfig.getApplication(appClass);
186     obj = appClass.newInstance();
187     gm = GroupManager.getGroupManager(obj);
188   }
189 
190 
191   ////////////////////////////////////////////////////////////////////////////////////////////
192   // ApplicationStarter methods
193   ////////////////////////////////////////////////////////////////////////////////////////////
194 
195   public void shutdown()
196   {
197     System.out.println(obj.getClass().getName() + " shutting down...");
198     System.exit(0);
199   }
200 
201 
202   ////////////////////////////////////////////////////////////////////////////////////////////
203   // Main method
204   ////////////////////////////////////////////////////////////////////////////////////////////
205 
206   public static void main(String[] args)
207     throws Exception
208   {
209     ConfigManager.init();
210     new ApplicationStarter(args);
211   }
212 
213 } // END ApplicationStarter