121 * activation descriptor for the activation identifier, id, determines
122 * the group in which the object should be activated and invokes the
123 * activate method on the object's activation group (described by the
124 * remote interface <code>ActivationInstantiator</code>). The
125 * activator initiates the execution of activation groups as
126 * necessary. For example, if an activation group for a specific group
127 * identifier is not already executing, the activator will spawn a
128 * child process for the activation group. <p>
129 *
130 * The activator is responsible for monitoring and detecting when
131 * activation groups fail so that it can remove stale remote references
132 * from its internal tables. <p>
133 *
134 * @author Ann Wollrath
135 * @since 1.2
136 */
137 public class Activation implements Serializable {
138
139 /** indicate compatibility with JDK 1.2 version of class */
140 private static final long serialVersionUID = 2921265612698155191L;
141
142 private static final byte MAJOR_VERSION = 1;
143 private static final byte MINOR_VERSION = 0;
144
145 /** exec policy object */
146 private static Object execPolicy;
147 private static Method execPolicyMethod;
148 private static boolean debugExec;
149
150 /** maps activation id to its respective group id */
151 private Map<ActivationID,ActivationGroupID> idTable =
152 new ConcurrentHashMap<>();
153 /** maps group id to its GroupEntry groups */
154 private Map<ActivationGroupID,GroupEntry> groupTable =
155 new ConcurrentHashMap<>();
156
157 private byte majorVersion = MAJOR_VERSION;
158 private byte minorVersion = MINOR_VERSION;
159
160 /** number of simultaneous group exec's */
161 private transient int groupSemaphore;
281 /**
282 * Previous versions used HashMap instead of ConcurrentHashMap.
283 * Replace any HashMaps found during deserialization with
284 * ConcurrentHashMaps.
285 */
286 private void readObject(ObjectInputStream ois)
287 throws IOException, ClassNotFoundException
288 {
289 ois.defaultReadObject();
290 if (! (groupTable instanceof ConcurrentHashMap)) {
291 groupTable = new ConcurrentHashMap<>(groupTable);
292 }
293 if (! (idTable instanceof ConcurrentHashMap)) {
294 idTable = new ConcurrentHashMap<>(idTable);
295 }
296 }
297
298 private static class SystemRegistryImpl extends RegistryImpl {
299
300 private static final String NAME = ActivationSystem.class.getName();
301 private final ActivationSystem systemStub;
302
303 SystemRegistryImpl(int port,
304 RMIClientSocketFactory csf,
305 RMIServerSocketFactory ssf,
306 ActivationSystem systemStub)
307 throws RemoteException
308 {
309 super(port, csf, ssf);
310 this.systemStub = systemStub;
311 }
312
313 /**
314 * Returns the activation system stub if the specified name
315 * matches the activation system's class name, otherwise
316 * returns the result of invoking super.lookup with the specified
317 * name.
318 */
319 public Remote lookup(String name)
320 throws RemoteException, NotBoundException
787 *
788 * WARNING: GroupEntry objects should not be written into log file
789 * updates. GroupEntrys are inner classes of Activation and they
790 * can not be serialized independent of this class. If the
791 * complete Activation system is written out as a log update, the
792 * point of having updates is nullified.
793 */
794 private class GroupEntry implements Serializable {
795
796 /** indicate compatibility with JDK 1.2 version of class */
797 private static final long serialVersionUID = 7222464070032993304L;
798 private static final int MAX_TRIES = 2;
799 private static final int NORMAL = 0;
800 private static final int CREATING = 1;
801 private static final int TERMINATE = 2;
802 private static final int TERMINATING = 3;
803
804 ActivationGroupDesc desc = null;
805 ActivationGroupID groupID = null;
806 long incarnation = 0;
807 Map<ActivationID,ObjectEntry> objects =
808 new HashMap<ActivationID,ObjectEntry>();
809 Set<ActivationID> restartSet = new HashSet<ActivationID>();
810
811 transient ActivationInstantiator group = null;
812 transient int status = NORMAL;
813 transient long waitTime = 0;
814 transient String groupName = null;
815 transient Process child = null;
816 transient boolean removed = false;
817 transient Watchdog watchdog = null;
818
819 GroupEntry(ActivationGroupID groupID, ActivationGroupDesc desc) {
820 this.groupID = groupID;
821 this.desc = desc;
822 }
823
824 void restartServices() {
825 Iterator<ActivationID> iter = null;
826
827 synchronized (this) {
828 if (restartSet.isEmpty()) {
829 return;
1040
1041 private void childGone() {
1042 if (child != null) {
1043 child = null;
1044 watchdog.dispose();
1045 watchdog = null;
1046 status = NORMAL;
1047 notifyAll();
1048 }
1049 }
1050
1051 private void terminate() {
1052 if (child != null && status != TERMINATING) {
1053 child.destroy();
1054 status = TERMINATING;
1055 waitTime = System.currentTimeMillis() + groupTimeout;
1056 notifyAll();
1057 }
1058 }
1059
1060 private void await() {
1061 while (true) {
1062 switch (status) {
1063 case NORMAL:
1064 return;
1065 case TERMINATE:
1066 terminate();
1067 case TERMINATING:
1068 try {
1069 child.exitValue();
1070 } catch (IllegalThreadStateException e) {
1071 long now = System.currentTimeMillis();
1072 if (waitTime > now) {
1073 try {
1074 wait(waitTime - now);
1075 } catch (InterruptedException ee) {
1076 }
1077 continue;
1078 }
1079 // REMIND: print message that group did not terminate?
1211 sb.append(argv[j]);
1212 }
1213 System.err.println(
1214 MessageFormat.format(
1215 getTextResource("rmid.exec.command"),
1216 sb.toString()));
1217 }
1218
1219 try {
1220 child = Runtime.getRuntime().exec(argv);
1221 status = CREATING;
1222 ++incarnation;
1223 watchdog = new Watchdog();
1224 watchdog.start();
1225 addLogRecord(new LogGroupIncarnation(id, incarnation));
1226
1227 // handle child I/O streams before writing to child
1228 PipeWriter.plugTogetherPair
1229 (child.getInputStream(), System.out,
1230 child.getErrorStream(), System.err);
1231
1232 MarshalOutputStream out =
1233 new MarshalOutputStream(child.getOutputStream());
1234 out.writeObject(id);
1235 out.writeObject(desc);
1236 out.writeLong(incarnation);
1237 out.flush();
1238 out.close();
1239
1240
1241 } catch (IOException e) {
1242 terminate();
1243 throw new ActivationException(
1244 "unable to create activation group", e);
1245 }
1246
1247 try {
1248 long now = System.currentTimeMillis();
1249 long stop = now + execTimeout;
1250 do {
1251 wait(stop - now);
1252 if (group != null) {
1253 return group;
1254 }
1255 now = System.currentTimeMillis();
1256 } while (status == CREATING && now < stop);
1257 } catch (InterruptedException e) {
1258 }
1335 shouldQuit = true;
1336 if (canInterrupt) {
1337 interrupt();
1338 }
1339 }
1340
1341 /**
1342 * Marks this thread as no longer needing to restart objects.
1343 */
1344 void noRestart() {
1345 shouldRestart = false;
1346 }
1347 }
1348 }
1349
1350 private String[] activationArgs(ActivationGroupDesc desc) {
1351 ActivationGroupDesc.CommandEnvironment cmdenv;
1352 cmdenv = desc.getCommandEnvironment();
1353
1354 // argv is the literal command to exec
1355 List<String> argv = new ArrayList<String>();
1356
1357 // Command name/path
1358 argv.add((cmdenv != null && cmdenv.getCommandPath() != null)
1359 ? cmdenv.getCommandPath()
1360 : command[0]);
1361
1362 // Group-specific command options
1363 if (cmdenv != null && cmdenv.getCommandOptions() != null) {
1364 argv.addAll(Arrays.asList(cmdenv.getCommandOptions()));
1365 }
1366
1367 // Properties become -D parameters
1368 Properties props = desc.getPropertyOverrides();
1369 if (props != null) {
1370 for (Enumeration<?> p = props.propertyNames();
1371 p.hasMoreElements();)
1372 {
1373 String name = (String) p.nextElement();
1374 /* Note on quoting: it would be wrong
1375 * here, since argv will be passed to
1940 File.createTempFile("rmid-err", null, null);
1941 PrintStream errStream =
1942 new PrintStream(new FileOutputStream(file));
1943 System.setErr(errStream);
1944 return null;
1945 }
1946 });
1947
1948 ServerSocket serverSocket =
1949 ((ServerSocketChannel) inheritedChannel).socket();
1950 port = serverSocket.getLocalPort();
1951 ssf = new ActivationServerSocketFactory(serverSocket);
1952
1953 System.err.println(new Date());
1954 System.err.println(getTextResource(
1955 "rmid.inherited.channel.info") +
1956 ": " + inheritedChannel);
1957 }
1958
1959 String log = null;
1960 List<String> childArgs = new ArrayList<String>();
1961
1962 /*
1963 * Parse arguments
1964 */
1965 for (int i = 0; i < args.length; i++) {
1966 if (args[i].equals("-port")) {
1967 if (ssf != null) {
1968 bomb(getTextResource("rmid.syntax.port.badarg"));
1969 }
1970 if ((i + 1) < args.length) {
1971 try {
1972 port = Integer.parseInt(args[++i]);
1973 } catch (NumberFormatException nfe) {
1974 bomb(getTextResource("rmid.syntax.port.badnumber"));
1975 }
1976 } else {
1977 bomb(getTextResource("rmid.syntax.port.missing"));
1978 }
1979
1980 } else if (args[i].equals("-log")) {
2014 String execPolicyClassName = AccessController.doPrivileged(
2015 new GetPropertyAction("sun.rmi.activation.execPolicy", null));
2016 if (execPolicyClassName == null) {
2017 if (!stop) {
2018 DefaultExecPolicy.checkConfiguration();
2019 }
2020 execPolicyClassName = "default";
2021 }
2022
2023 /**
2024 * Initialize method for activation exec policy.
2025 */
2026 if (!execPolicyClassName.equals("none")) {
2027 if (execPolicyClassName.equals("") ||
2028 execPolicyClassName.equals("default"))
2029 {
2030 execPolicyClassName = DefaultExecPolicy.class.getName();
2031 }
2032
2033 try {
2034 Class<?> execPolicyClass =
2035 RMIClassLoader.loadClass(execPolicyClassName);
2036 execPolicy = execPolicyClass.newInstance();
2037 execPolicyMethod =
2038 execPolicyClass.getMethod("checkExecCommand",
2039 ActivationGroupDesc.class,
2040 String[].class);
2041 } catch (Exception e) {
2042 if (debugExec) {
2043 System.err.println(
2044 getTextResource("rmid.exec.policy.exception"));
2045 e.printStackTrace();
2046 }
2047 bomb(getTextResource("rmid.exec.policy.invalid"));
2048 }
2049 }
2050
2051 if (stop == true) {
2052 final int finalPort = port;
2053 AccessController.doPrivileged(new PrivilegedAction<Void>() {
2054 public Void run() {
2055 System.setProperty("java.rmi.activation.port",
2106 }
2107 if (Activation.resources == null) {
2108 // throwing an Error is a bit extreme, methinks
2109 return ("[missing resource file: " + key + "]");
2110 }
2111 }
2112
2113 String val = null;
2114 try {
2115 val = Activation.resources.getString (key);
2116 } catch (MissingResourceException mre) {
2117 }
2118
2119 if (val == null) {
2120 return ("[missing resource: " + key + "]");
2121 } else {
2122 return val;
2123 }
2124 }
2125
2126 /*
2127 * Dijkstra semaphore operations to limit the number of subprocesses
2128 * rmid attempts to make at once.
2129 */
2130 /**
2131 * Acquire the group semaphore and return a group name. Each
2132 * Pstartgroup must be followed by a Vstartgroup. The calling thread
2133 * will wait until there are fewer than <code>N</code> other threads
2134 * holding the group semaphore. The calling thread will then acquire
2135 * the semaphore and return.
2136 */
2137 private synchronized String Pstartgroup() throws ActivationException {
2138 while (true) {
2139 checkShutdown();
2140 // Wait until positive, then decrement.
2141 if (groupSemaphore > 0) {
2142 groupSemaphore--;
2143 return "Group-" + groupCounter++;
2144 }
2145
|
121 * activation descriptor for the activation identifier, id, determines
122 * the group in which the object should be activated and invokes the
123 * activate method on the object's activation group (described by the
124 * remote interface <code>ActivationInstantiator</code>). The
125 * activator initiates the execution of activation groups as
126 * necessary. For example, if an activation group for a specific group
127 * identifier is not already executing, the activator will spawn a
128 * child process for the activation group. <p>
129 *
130 * The activator is responsible for monitoring and detecting when
131 * activation groups fail so that it can remove stale remote references
132 * from its internal tables. <p>
133 *
134 * @author Ann Wollrath
135 * @since 1.2
136 */
137 public class Activation implements Serializable {
138
139 /** indicate compatibility with JDK 1.2 version of class */
140 private static final long serialVersionUID = 2921265612698155191L;
141 private static final byte MAJOR_VERSION = 1;
142 private static final byte MINOR_VERSION = 0;
143
144 /** exec policy object */
145 private static Object execPolicy;
146 private static Method execPolicyMethod;
147 private static boolean debugExec;
148
149 /** maps activation id to its respective group id */
150 private Map<ActivationID,ActivationGroupID> idTable =
151 new ConcurrentHashMap<>();
152 /** maps group id to its GroupEntry groups */
153 private Map<ActivationGroupID,GroupEntry> groupTable =
154 new ConcurrentHashMap<>();
155
156 private byte majorVersion = MAJOR_VERSION;
157 private byte minorVersion = MINOR_VERSION;
158
159 /** number of simultaneous group exec's */
160 private transient int groupSemaphore;
280 /**
281 * Previous versions used HashMap instead of ConcurrentHashMap.
282 * Replace any HashMaps found during deserialization with
283 * ConcurrentHashMaps.
284 */
285 private void readObject(ObjectInputStream ois)
286 throws IOException, ClassNotFoundException
287 {
288 ois.defaultReadObject();
289 if (! (groupTable instanceof ConcurrentHashMap)) {
290 groupTable = new ConcurrentHashMap<>(groupTable);
291 }
292 if (! (idTable instanceof ConcurrentHashMap)) {
293 idTable = new ConcurrentHashMap<>(idTable);
294 }
295 }
296
297 private static class SystemRegistryImpl extends RegistryImpl {
298
299 private static final String NAME = ActivationSystem.class.getName();
300 private static final long serialVersionUID = 4877330021609408794L;
301 private final ActivationSystem systemStub;
302
303 SystemRegistryImpl(int port,
304 RMIClientSocketFactory csf,
305 RMIServerSocketFactory ssf,
306 ActivationSystem systemStub)
307 throws RemoteException
308 {
309 super(port, csf, ssf);
310 this.systemStub = systemStub;
311 }
312
313 /**
314 * Returns the activation system stub if the specified name
315 * matches the activation system's class name, otherwise
316 * returns the result of invoking super.lookup with the specified
317 * name.
318 */
319 public Remote lookup(String name)
320 throws RemoteException, NotBoundException
787 *
788 * WARNING: GroupEntry objects should not be written into log file
789 * updates. GroupEntrys are inner classes of Activation and they
790 * can not be serialized independent of this class. If the
791 * complete Activation system is written out as a log update, the
792 * point of having updates is nullified.
793 */
794 private class GroupEntry implements Serializable {
795
796 /** indicate compatibility with JDK 1.2 version of class */
797 private static final long serialVersionUID = 7222464070032993304L;
798 private static final int MAX_TRIES = 2;
799 private static final int NORMAL = 0;
800 private static final int CREATING = 1;
801 private static final int TERMINATE = 2;
802 private static final int TERMINATING = 3;
803
804 ActivationGroupDesc desc = null;
805 ActivationGroupID groupID = null;
806 long incarnation = 0;
807 Map<ActivationID,ObjectEntry> objects = new HashMap<>();
808 Set<ActivationID> restartSet = new HashSet<>();
809
810 transient ActivationInstantiator group = null;
811 transient int status = NORMAL;
812 transient long waitTime = 0;
813 transient String groupName = null;
814 transient Process child = null;
815 transient boolean removed = false;
816 transient Watchdog watchdog = null;
817
818 GroupEntry(ActivationGroupID groupID, ActivationGroupDesc desc) {
819 this.groupID = groupID;
820 this.desc = desc;
821 }
822
823 void restartServices() {
824 Iterator<ActivationID> iter = null;
825
826 synchronized (this) {
827 if (restartSet.isEmpty()) {
828 return;
1039
1040 private void childGone() {
1041 if (child != null) {
1042 child = null;
1043 watchdog.dispose();
1044 watchdog = null;
1045 status = NORMAL;
1046 notifyAll();
1047 }
1048 }
1049
1050 private void terminate() {
1051 if (child != null && status != TERMINATING) {
1052 child.destroy();
1053 status = TERMINATING;
1054 waitTime = System.currentTimeMillis() + groupTimeout;
1055 notifyAll();
1056 }
1057 }
1058
1059 /*
1060 * Fallthrough from TERMINATE to TERMINATING
1061 * is intentional
1062 */
1063 @SuppressWarnings("fallthrough")
1064 private void await() {
1065 while (true) {
1066 switch (status) {
1067 case NORMAL:
1068 return;
1069 case TERMINATE:
1070 terminate();
1071 case TERMINATING:
1072 try {
1073 child.exitValue();
1074 } catch (IllegalThreadStateException e) {
1075 long now = System.currentTimeMillis();
1076 if (waitTime > now) {
1077 try {
1078 wait(waitTime - now);
1079 } catch (InterruptedException ee) {
1080 }
1081 continue;
1082 }
1083 // REMIND: print message that group did not terminate?
1215 sb.append(argv[j]);
1216 }
1217 System.err.println(
1218 MessageFormat.format(
1219 getTextResource("rmid.exec.command"),
1220 sb.toString()));
1221 }
1222
1223 try {
1224 child = Runtime.getRuntime().exec(argv);
1225 status = CREATING;
1226 ++incarnation;
1227 watchdog = new Watchdog();
1228 watchdog.start();
1229 addLogRecord(new LogGroupIncarnation(id, incarnation));
1230
1231 // handle child I/O streams before writing to child
1232 PipeWriter.plugTogetherPair
1233 (child.getInputStream(), System.out,
1234 child.getErrorStream(), System.err);
1235 try (MarshalOutputStream out =
1236 new MarshalOutputStream(child.getOutputStream())) {
1237 out.writeObject(id);
1238 out.writeObject(desc);
1239 out.writeLong(incarnation);
1240 out.flush();
1241 }
1242
1243
1244 } catch (IOException e) {
1245 terminate();
1246 throw new ActivationException(
1247 "unable to create activation group", e);
1248 }
1249
1250 try {
1251 long now = System.currentTimeMillis();
1252 long stop = now + execTimeout;
1253 do {
1254 wait(stop - now);
1255 if (group != null) {
1256 return group;
1257 }
1258 now = System.currentTimeMillis();
1259 } while (status == CREATING && now < stop);
1260 } catch (InterruptedException e) {
1261 }
1338 shouldQuit = true;
1339 if (canInterrupt) {
1340 interrupt();
1341 }
1342 }
1343
1344 /**
1345 * Marks this thread as no longer needing to restart objects.
1346 */
1347 void noRestart() {
1348 shouldRestart = false;
1349 }
1350 }
1351 }
1352
1353 private String[] activationArgs(ActivationGroupDesc desc) {
1354 ActivationGroupDesc.CommandEnvironment cmdenv;
1355 cmdenv = desc.getCommandEnvironment();
1356
1357 // argv is the literal command to exec
1358 List<String> argv = new ArrayList<>();
1359
1360 // Command name/path
1361 argv.add((cmdenv != null && cmdenv.getCommandPath() != null)
1362 ? cmdenv.getCommandPath()
1363 : command[0]);
1364
1365 // Group-specific command options
1366 if (cmdenv != null && cmdenv.getCommandOptions() != null) {
1367 argv.addAll(Arrays.asList(cmdenv.getCommandOptions()));
1368 }
1369
1370 // Properties become -D parameters
1371 Properties props = desc.getPropertyOverrides();
1372 if (props != null) {
1373 for (Enumeration<?> p = props.propertyNames();
1374 p.hasMoreElements();)
1375 {
1376 String name = (String) p.nextElement();
1377 /* Note on quoting: it would be wrong
1378 * here, since argv will be passed to
1943 File.createTempFile("rmid-err", null, null);
1944 PrintStream errStream =
1945 new PrintStream(new FileOutputStream(file));
1946 System.setErr(errStream);
1947 return null;
1948 }
1949 });
1950
1951 ServerSocket serverSocket =
1952 ((ServerSocketChannel) inheritedChannel).socket();
1953 port = serverSocket.getLocalPort();
1954 ssf = new ActivationServerSocketFactory(serverSocket);
1955
1956 System.err.println(new Date());
1957 System.err.println(getTextResource(
1958 "rmid.inherited.channel.info") +
1959 ": " + inheritedChannel);
1960 }
1961
1962 String log = null;
1963 List<String> childArgs = new ArrayList<>();
1964
1965 /*
1966 * Parse arguments
1967 */
1968 for (int i = 0; i < args.length; i++) {
1969 if (args[i].equals("-port")) {
1970 if (ssf != null) {
1971 bomb(getTextResource("rmid.syntax.port.badarg"));
1972 }
1973 if ((i + 1) < args.length) {
1974 try {
1975 port = Integer.parseInt(args[++i]);
1976 } catch (NumberFormatException nfe) {
1977 bomb(getTextResource("rmid.syntax.port.badnumber"));
1978 }
1979 } else {
1980 bomb(getTextResource("rmid.syntax.port.missing"));
1981 }
1982
1983 } else if (args[i].equals("-log")) {
2017 String execPolicyClassName = AccessController.doPrivileged(
2018 new GetPropertyAction("sun.rmi.activation.execPolicy", null));
2019 if (execPolicyClassName == null) {
2020 if (!stop) {
2021 DefaultExecPolicy.checkConfiguration();
2022 }
2023 execPolicyClassName = "default";
2024 }
2025
2026 /**
2027 * Initialize method for activation exec policy.
2028 */
2029 if (!execPolicyClassName.equals("none")) {
2030 if (execPolicyClassName.equals("") ||
2031 execPolicyClassName.equals("default"))
2032 {
2033 execPolicyClassName = DefaultExecPolicy.class.getName();
2034 }
2035
2036 try {
2037 Class<?> execPolicyClass = getRMIClass(execPolicyClassName);
2038
2039 execPolicy = execPolicyClass.newInstance();
2040 execPolicyMethod =
2041 execPolicyClass.getMethod("checkExecCommand",
2042 ActivationGroupDesc.class,
2043 String[].class);
2044 } catch (Exception e) {
2045 if (debugExec) {
2046 System.err.println(
2047 getTextResource("rmid.exec.policy.exception"));
2048 e.printStackTrace();
2049 }
2050 bomb(getTextResource("rmid.exec.policy.invalid"));
2051 }
2052 }
2053
2054 if (stop == true) {
2055 final int finalPort = port;
2056 AccessController.doPrivileged(new PrivilegedAction<Void>() {
2057 public Void run() {
2058 System.setProperty("java.rmi.activation.port",
2109 }
2110 if (Activation.resources == null) {
2111 // throwing an Error is a bit extreme, methinks
2112 return ("[missing resource file: " + key + "]");
2113 }
2114 }
2115
2116 String val = null;
2117 try {
2118 val = Activation.resources.getString (key);
2119 } catch (MissingResourceException mre) {
2120 }
2121
2122 if (val == null) {
2123 return ("[missing resource: " + key + "]");
2124 } else {
2125 return val;
2126 }
2127 }
2128
2129 @SuppressWarnings("deprecation")
2130 private static Class<?> getRMIClass(String execPolicyClassName) throws Exception {
2131 return RMIClassLoader.loadClass(execPolicyClassName);
2132 }
2133 /*
2134 * Dijkstra semaphore operations to limit the number of subprocesses
2135 * rmid attempts to make at once.
2136 */
2137 /**
2138 * Acquire the group semaphore and return a group name. Each
2139 * Pstartgroup must be followed by a Vstartgroup. The calling thread
2140 * will wait until there are fewer than <code>N</code> other threads
2141 * holding the group semaphore. The calling thread will then acquire
2142 * the semaphore and return.
2143 */
2144 private synchronized String Pstartgroup() throws ActivationException {
2145 while (true) {
2146 checkShutdown();
2147 // Wait until positive, then decrement.
2148 if (groupSemaphore > 0) {
2149 groupSemaphore--;
2150 return "Group-" + groupCounter++;
2151 }
2152
|