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