1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 /**
18 * @author Serguei S.Zapreyev
19 *
20 */
21
22 /**
23 * ###############################################################################
24 * ###############################################################################
25 * TODO LIST:
26 * 1. Provide correct processing the case if process isn't started because of some
27 * reason
28 * 2. Clean and develop the native support
29 * 3. Think of the default/undefault buffering
30 * 3. Runtime.SubProcess.SubInputStream.read(b, off, len) and
31 * Runtime.SubProcess.SubErrorStream.read(b, off, len) should be effectively
32 * reimplemented on the native side.
33 * ###############################################################################
34 * ###############################################################################
35 */
36
37 package java.lang;
38
39 import java.util.StringTokenizer;
40 import java.io.BufferedInputStream;
41 import java.io.BufferedOutputStream;
42 import java.io.File;
43 import java.io.InputStream;
44 import java.io.OutputStream;
45 import java.io.IOException;
46 import java.lang.UnsatisfiedLinkError;
47 import java.lang.VMExecutionEngine;
48 import java.lang.VMMemoryManager;
49 import java.util.ArrayList;
50 import org.apache.harmony.vm.VMStack;
51 import org.apache.harmony.kernel.vm.VM;
52 import org.apache.harmony.luni.util.DeleteOnExit;
53 import org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl;
54 import org.apache.harmony.lang.RuntimePermissionCollection;
55
56 /**
57 * @com.intel.drl.spec_ref
58 */
59 public class Runtime
60 {
61
62 //--------------------------------------------------------------------------------
63 // Nested protected Runtime.SubProcess class:
64 //--------------------------------------------------------------------------------
65
66 static final class SubProcess extends Process {
67
68
69 final static class SubInputStream extends InputStream {
70
71 long streamHandle;
72
73 /**
74 * Constructs a new SubInputStream instance.
75 */
76 SubInputStream() {
77 this.streamHandle = -1;
78 }
79
80 /**
81 * Reads the next byte of data from the input stream....
82 *
83 * @see int read() from InputStream
84 */
85 private final native int readInputByte0(long handle) throws IOException;
86
87 public final int read() throws IOException {
88 return readInputByte0(this.streamHandle);
89 }
90
91 /**
92 * Returns the number of bytes that can be read (or skipped over) from
93 * this input stream without blocking by the next caller
94 * of a method for this input stream...
95 *
96 * @see int available() from InputStream
97 */
98 private final native int available0(long handle);
99
100 public final int available() throws IOException {
101 return available0(this.streamHandle);
102 }
103
104 /**
105 * Reads len bytes from input stream ...
106 *
107 * @see void read(byte[], int, int) from InputStream
108 */
109 public int read(byte[] b, int off, int len) throws IOException {
110 if (b == null) {
111 throw new NullPointerException();
112 }
113
114 if (off < 0 || len < 0 || off + len > b.length) {
115 throw new IndexOutOfBoundsException();
116 }
117
118 if (len == 0) {
119 return 0;
120 }
121 int c = read();
122 if (c == -1) {
123 return -1;
124 }
125 b[off] = (byte) c;
126
127 int i = 1;
128 for(; i < len; i++) {
129 try {
130 if (available() != 0) {
131 int r = read();
132 if (r != -1) {
133 b[off + i] = (byte) r;
134 continue;
135 }
136 return i;
137 }
138 } catch(IOException e) {
139 break; //If any subsequent call to read() results in a IOException
140 }
141 break; //but a smaller number may be read, possibly zero.
142 }
143 return i;
144 }
145
146 /**
147 * Closes this input stream and releases any system resources associated
148 * with the stream.
149 *
150 * @see void close() from InputStream
151 */
152 private final native void close0(long handle) throws IOException;
153
154 public final synchronized void close() throws IOException {
155 if (streamHandle == -1) return;
156 close0(streamHandle);
157 streamHandle = -1;
158 }
159
160 protected void finalize() throws Throwable {
161 close();
162 }
163 }
164
165 //--------------------------------------------------------------------------------
166 // Nested Class Runtime.SubProcess.SubOutputStream :
167 //--------------------------------------------------------------------------------
168
169 /**
170 * Extends OutputStream class.
171 */
172 final static class SubOutputStream extends OutputStream {
173
174 long streamHandle;
175
176 /**
177 * Constructs a new SubOutputStream instance.
178 */
179 SubOutputStream() {
180 this.streamHandle = -1;
181 }
182
183 /**
184 * Writes the specified byte to this output stream ...
185 *
186 * @see void write(int) from OutputStream
187 */
188 private final native void writeOutputByte0(long handle, int bt);
189
190 public final void write(int b) throws IOException {
191 writeOutputByte0(this.streamHandle, b);
192 }
193
194 /**
195 * Writes len bytes from the specified byte array starting at
196 * offset off to this output stream ...
197 *
198 * @see void write(byte[], int, int) from OutputStream
199 */
200 private final native void writeOutputBytes0(long handle, byte[] b, int off, int len);
201
202 public final void write(byte[] b, int off, int len) throws IOException {
203 if (b == null) {
204 throw new NullPointerException();
205 }
206
207 if (off < 0 || len < 0 || off + len > b.length) {
208 throw new IndexOutOfBoundsException();
209 }
210
211 writeOutputBytes0(this.streamHandle, b, off, len);
212 }
213
214 /**
215 * Writes b.length bytes from the specified byte array to this output stream...
216 *
217 * @see void write(byte[]) from OutputStream
218 */
219 public final void write(byte[] b) throws IOException {
220 write(b, 0, b.length);
221 }
222
223 /**
224 * Flushes this output stream and forces any buffered output
225 * bytes to be written out ...
226 *
227 * @see void flush() from OutputStream
228 */
229 private final native void flush0(long handle);
230
231 public final void flush() throws IOException {
232 flush0(this.streamHandle);
233 }
234
235 /**
236 * Closes this output stream and releases any system resources
237 * associated with this stream ...
238 *
239 * @see void close() from OutputStream
240 */
241 private final native void close0(long handle);
242
243 public final synchronized void close() throws IOException {
244 if (streamHandle == -1) return;
245 close0(streamHandle);
246 streamHandle = -1;
247 }
248
249 protected void finalize() throws Throwable {
250 close();
251 }
252 }
253
254 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
255 ///////////////////////////////////// Runtime.SubProcess BODY //////////////////////////////////
256 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
257
258 private int processHandle;
259 private int processExitCode;
260 private OutputStream os;
261 private InputStream is;
262 private InputStream es;
263
264 /**
265 * An application cannot create its own instance of this class.
266 */
267 protected SubProcess() {
268 this.processHandle = -1;
269 this.processExitCode = 0;
270 this.os = null;
271 this.is = null;
272 this.es = null;
273 }
274
275 private final native void close0(int handle);
276
277 protected void finalize() throws Throwable {
278 if (processHandle != -1)
279 close0(this.processHandle);
280 }
281
282 /**
283 * @see OutputStream.getOutputStream() from Process
284 */
285 public final OutputStream getOutputStream() {
286 return os;
287 }
288
289 /**
290 * @see InputStream.getInputStream() from Process
291 */
292 public final InputStream getInputStream() {
293 return is;
294 }
295
296 /**
297 * @see InputStream getErrorStream() from Process
298 */
299 public final InputStream getErrorStream() {
300 return es;
301 }
302
303 private final native boolean getState0(int thisProcessHandle);
304
305 private final native void createProcess0(Object[] cmdarray, Object[] envp, String dir, long[] ia);
306
307 protected final void execVM(String[] cmdarray, String[] envp, String dir) throws IOException {
308 // Do all java heap allocation first, in order to throw OutOfMemory
309 // exception early, before we have actually executed the process.
310 // Otherwise we should do somewhat complicated cleanup.
311 SubProcess.SubOutputStream os1 = new SubProcess.SubOutputStream();
312 SubProcess.SubInputStream is1 = new SubProcess.SubInputStream();
313 SubProcess.SubInputStream es1 = new SubProcess.SubInputStream();
314
315 long[] la = new long[4];
316 createProcess0(cmdarray, envp, dir, la);
317 if (la[0] == 0) {
318 String cmd = null;
319 for(int i = 0; i < cmdarray.length; i++) {
320 if (i == 0) {
321 cmd = "\"" + cmdarray[i] + "\"";
322 } else {
323 cmd = cmd + " " + cmdarray[i];
324 }
325 }
326 throw new IOException("The creation of the Process has just failed: " + cmd);
327 }
328 this.processHandle = (int)la[0];
329 os1.streamHandle = la[1];
330 is1.streamHandle = la[2];
331 es1.streamHandle = la[3];
332 os = new BufferedOutputStream(os1);
333 is = new BufferedInputStream(is1);
334 es = new BufferedInputStream(es1);
335 }
336
337 /**
338 * @seeint waitFor() from Process
339 */
340 public int waitFor() throws InterruptedException {
341 while (true) {
342 synchronized (this) {
343 if (getState0(processHandle)) break;
344 }
345 Thread.sleep(50);
346 }
347
348 return processExitCode;
349 }
350
351 /**
352 * @see int exitValue() from Process
353 */
354 public synchronized int exitValue() throws IllegalThreadStateException {
355 if (!getState0(processHandle)) {
356 throw new IllegalThreadStateException("process has not exited");
357 }
358
359 return processExitCode;
360 }
361
362 /**
363 * @see void destroy() from Process
364 */
365 private final native void destroy0(int thisProcessHandle);
366
367 public synchronized final void destroy() {
368 destroy0(processHandle);
369 }
370
371 }
372
373 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
374 ////////////////////////////////////////// RUNTIME BODY ////////////////////////////////////////
375 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
376
377 /**
378 * "Every Java application has a single instance of class Runtime ..."
379 */
380 private static Runtime thisApplicationRuntime = new Runtime();
381
382 private static ArrayList<Thread> hooksList = new ArrayList<Thread>();
383
384 /**
385 * 0 - normal work
386 * 1 - being shutdown sequence running
387 * 2 - being finalizing
388 */
389 private static int VMState = 0;
390
391 static boolean finalizeOnExit = false;
392
393 /**
394 * An application cannot create its own instance of this class.
395 */
396 private Runtime() {
397 }
398
399 /**
400 * @com.intel.drl.spec_ref
401 */
402 public static Runtime getRuntime() {
403 return thisApplicationRuntime;
404 }
405
406 void execShutdownSequence() {
407 synchronized (hooksList) {
408 if (VMState > 0) {
409 return;
410 }
411 try {
412 // Phase1: Execute all registered hooks.
413 VMState = 1;
414 for (Thread hook : hooksList) {
415 hook.start();
416 }
417
418 for (Thread hook : hooksList) {
419 while (true){
420 try {
421 hook.join();
422 break;
423 } catch (InterruptedException e) {
424 continue;
425 }
426 }
427 }
428 // Phase2: Execute all finalizers if nessesary.
429 VMState = 2;
430 FinalizerThread.shutdown(finalizeOnExit);
431
432 // Close connections.
433 if (VM.closeJars) {
434 JarURLConnectionImpl.closeCachedFiles();
435 }
436
437 // Delete files.
438 if (VM.deleteOnExit) {
439 DeleteOnExit.deleteOnExit();
440 }
441 } catch (Throwable e) {
442 // just catch all exceptions
443 }
444 }
445 }
446
447 /**
448 * @com.intel.drl.spec_ref
449 */
450 public void exit(int status) throws SecurityException {
451 SecurityManager sm = System.getSecurityManager();
452 if (sm != null) {
453 sm.checkExit(status);
454 }
455 // Halt the VM if it is running finalizers.
456 if (VMState == 2 && finalizeOnExit == true && status != 0) {
457 halt(status);
458 }
459
460 execShutdownSequence();
461 // No need to invoke finalizers one more time.
462 // vvvvv
463 VMExecutionEngine.exit(status, false);
464 }
465
466 /**
467 * @com.intel.drl.spec_ref
468 */
469 public void addShutdownHook(Thread hook) {
470 SecurityManager sm = System.getSecurityManager();
471 if (sm != null) {
472 sm.checkPermission(RuntimePermissionCollection.SHUTDOWN_HOOKS_PERMISSION);
473 }
474 // Check hook for null
475 if (hook == null)
476 throw new NullPointerException("null is not allowed here");
477
478 if (hook.getState() != Thread.State.NEW) {
479 throw new IllegalArgumentException();
480 }
481 if (VMState > 0) {
482 throw new IllegalStateException();
483 }
484 synchronized (hooksList) {
485 if (hooksList.contains(hook)) {
486 throw new IllegalArgumentException();
487 }
488 hooksList.add(hook);
489 }
490 }
491
492 /**
493 * @com.intel.drl.spec_ref
494 */
495 public boolean removeShutdownHook(Thread hook) {
496 SecurityManager sm = System.getSecurityManager();
497 if (sm != null) {
498 sm.checkPermission(RuntimePermissionCollection.SHUTDOWN_HOOKS_PERMISSION);
499 }
500 // Check hook for null
501 if (hook == null)
502 throw new NullPointerException("null is not allowed here");
503
504 if (VMState > 0) {
505 throw new IllegalStateException();
506 }
507 synchronized (hooksList) {
508 return hooksList.remove(hook);
509 }
510 }
511
512 /**
513 * @com.intel.drl.spec_ref
514 */
515 public void halt(int status) {
516 SecurityManager sm = System.getSecurityManager();
517
518 if (sm != null) {
519 sm.checkExit(status);
520 }
521 VMExecutionEngine.exit(status, false);
522 }
523
524 /**
525 * @com.intel.drl.spec_ref
526 * @deprecated
527 */
528 public static void runFinalizersOnExit(boolean value) {
529 SecurityManager sm = System.getSecurityManager();
530 if (sm != null) {
531 sm.checkExit(0);
532 }
533 synchronized(hooksList) {
534 finalizeOnExit = value;
535 }
536 }
537
538 /**
539 * @com.intel.drl.spec_ref
540 */
541 public Process exec(String command) throws IOException {
542 return exec(command, null, null);
543 }
544
545 /**
546 * @com.intel.drl.spec_ref
547 */
548 public Process exec(String cmd, String[] envp) throws IOException {
549 return exec(cmd, envp, null);
550 }
551
552 /**
553 * @com.intel.drl.spec_ref
554 */
555 public Process exec(String command, String[] envp, File dir) throws IOException {
556 if (command == null) {
557 throw new NullPointerException();
558 }
559 if (command.length() == 0) {
560 throw new IllegalArgumentException();
561 }
562 if (envp != null) {
563 if (envp.length != 0) {
564 for (int i = 0; i < envp.length; i++) {
565 if (envp[i] == null) {
566 throw new NullPointerException("An element of envp shouldn't be empty.");
567 }
568 }
569 } else {
570 envp = null;
571 }
572 }
573
574 StringTokenizer st = new StringTokenizer(command);
575 String[] cmdarray = new String[st.countTokens()];
576 int i = 0;
577
578 while (st.hasMoreTokens()) {
579 cmdarray[i++] = st.nextToken();
580
581 }
582
583 return exec(cmdarray, envp, dir);
584
585 }
586
587 /**
588 * @com.intel.drl.spec_ref
589 */
590 public Process exec(String[] cmdarray) throws IOException {
591 return exec(cmdarray, null, null);
592
593 }
594
595 /**
596 * @com.intel.drl.spec_ref
597 */
598 public Process exec(String[] cmdarray, String[] envp) throws IOException, NullPointerException, IndexOutOfBoundsException, SecurityException {
599 return exec(cmdarray, envp, null);
600
601 }
602
603 /**
604 * @com.intel.drl.spec_ref
605 */
606 public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException {
607 SecurityManager currentSecurity = System.getSecurityManager();
608
609 if (currentSecurity != null) {
610 currentSecurity.checkExec(cmdarray[0]);
611 }
612
613 if (cmdarray == null) {
614 throw new NullPointerException("Command argument shouldn't be empty.");
615 }
616 if (cmdarray.length == 0) {
617 throw new IndexOutOfBoundsException();
618 }
619 for (int i = 0; i < cmdarray.length; i++) {
620 if (cmdarray[i] == null) {
621 throw new NullPointerException("An element of cmdarray shouldn't be empty.");
622 }
623 }
624 if (envp != null) {
625 if (envp.length != 0) {
626 for (int i = 0; i < envp.length; i++) {
627 if (envp[i] == null) {
628 throw new NullPointerException("An element of envp shouldn't be empty.");
629 }
630 }
631 } else {
632 envp = null;
633 }
634 }
635
636 String dirPathName = (dir != null ? dir.getPath() : null);
637
638 SubProcess sp = new SubProcess();
639
640 sp.execVM(cmdarray, envp, dirPathName);
641
642 return sp;
643
644 }
645
646 /**
647 * @com.intel.drl.spec_ref
648 */
649 public int availableProcessors() {
650 return VMExecutionEngine.getAvailableProcessors();
651 }
652
653 /**
654 * @com.intel.drl.spec_ref
655 */
656 public long freeMemory() {
657 return VMMemoryManager.getFreeMemory();
658 }
659
660 /**
661 * @com.intel.drl.spec_ref
662 */
663 public long totalMemory() {
664 return VMMemoryManager.getTotalMemory();
665 }
666
667 /**
668 * @com.intel.drl.spec_ref
669 */
670 public long maxMemory() {
671 return VMMemoryManager.getMaxMemory();
672 }
673
674 /**
675 * @com.intel.drl.spec_ref
676 */
677 public void gc() {
678 VMMemoryManager.runGC();
679 }
680
681 /**
682 * @com.intel.drl.spec_ref
683 */
684 public void runFinalization() {
685 VMMemoryManager.runFinalization();
686 }
687
688 /**
689 * @com.intel.drl.spec_ref
690 */
691 public void traceInstructions(boolean on) {
692 VMExecutionEngine.traceInstructions(on);
693 }
694
695 /**
696 * @com.intel.drl.spec_ref
697 */
698 public void traceMethodCalls(boolean on) {
699 VMExecutionEngine.traceMethodCalls(on);
700 }
701
702 /**
703 * @com.intel.drl.spec_ref
704 */
705 public void load(String filename) throws SecurityException, UnsatisfiedLinkError {
706 load0(filename, VMClassRegistry.getClassLoader(VMStack.getCallerClass(0)), true);
707 }
708
709 void load0(String filename, ClassLoader cL, boolean check) throws SecurityException, UnsatisfiedLinkError {
710 if (check) {
711 if (filename == null) {
712 throw new NullPointerException();
713 }
714
715 SecurityManager currentSecurity = System.getSecurityManager();
716
717 if (currentSecurity != null) {
718 currentSecurity.checkLink(filename);
719 }
720 }
721 VMClassRegistry.loadLibrary(filename, cL); // Should throw UnsatisfiedLinkError if needs.
722 }
723
724 /**
725 * @com.intel.drl.spec_ref
726 */
727 public void loadLibrary(String libname) throws SecurityException, UnsatisfiedLinkError {
728 loadLibrary0(libname, VMClassRegistry.getClassLoader(VMStack.getCallerClass(0)), true);
729 }
730
731 void loadLibrary0(String libname, ClassLoader cL, boolean check) throws SecurityException, UnsatisfiedLinkError {
732 if (check) {
733 if (libname == null) {
734 throw new NullPointerException();
735 }
736
737 SecurityManager currentSecurity = System.getSecurityManager();
738
739 if (currentSecurity != null) {
740 currentSecurity.checkLink(libname);
741 }
742 }
743
744 String libFullName = null;
745
746 if (cL!=null) {
747 libFullName = cL.findLibrary(libname);
748 }
749 if (libFullName == null) {
750 String allPaths = null;
751
752 //XXX: should we think hard about security policy for this block?:
753 String jlp = System.getProperty("java.library.path");
754 String vblp = System.getProperty("vm.boot.library.path");
755 String udp = System.getProperty("user.dir");
756 String pathSeparator = System.getProperty("path.separator");
757 String fileSeparator = System.getProperty("file.separator");
758 allPaths = (jlp!=null?jlp:"")+(vblp!=null?pathSeparator+vblp:"")+(udp!=null?pathSeparator+udp:"");
759
760 if (allPaths.length()==0) {
761 throw new UnsatisfiedLinkError("Can not find the library: " +
762 libname);
763 }
764
765 //String[] paths = allPaths.split(pathSeparator);
766 String[] paths;
767 {
768 ArrayList<String> res = new ArrayList<String>();
769 int curPos = 0;
770 int l = pathSeparator.length();
771 int i = allPaths.indexOf(pathSeparator);
772 int in = 0;
773 while (i != -1) {
774 String s = allPaths.substring(curPos, i);
775 res.add(s);
776 in++;
777 curPos = i + l;
778 i = allPaths.indexOf(pathSeparator, curPos);
779 }
780
781 if (curPos <= allPaths.length()) {
782 String s = allPaths.substring(curPos, allPaths.length());
783 in++;
784 res.add(s);
785 }
786
787 paths = (String[]) res.toArray(new String[in]);
788 }
789
790 libname = System.mapLibraryName(libname);
791 for (int i=0; i<paths.length; i++) {
792 if (paths[i]==null) {
793 continue;
794 }
795 libFullName = paths[i] + fileSeparator + libname;
796 try {
797 this.load0(libFullName, cL, false);
798 return;
799 } catch (UnsatisfiedLinkError e) {
800 }
801 }
802 } else {
803 this.load0(libFullName, cL, false);
804 return;
805 }
806 throw new UnsatisfiedLinkError("Can not find the library: " +
807 libname);
808 }
809
810 /**
811 * @com.intel.drl.spec_ref
812 * @deprecated
813 */
814 public InputStream getLocalizedInputStream(InputStream in) {
815 //XXX: return new BufferedInputStream( (InputStream) (Object) new InputStreamReader( in ) );
816 return in;
817 }
818
819 /**
820 * @com.intel.drl.spec_ref
821 * @deprecated
822 */
823 public OutputStream getLocalizedOutputStream(OutputStream out) {
824 //XXX: return new BufferedOutputStream( (OutputStream) (Object) new OutputStreamWriter( out ) );
825 return out;
826 }
827 }