import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.StringReader;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;


/**
* Create and run THREAD_COUNT PHP threads, concurrently accessing a
* shared resource.
* 
* Create 5 script engines, passing each a shared resource allocated
* from Java. Each script engine has to implement Runnable.
*
* Java accesses the Runnable script engine using
* scriptEngine.getInterface() and calls thread.start() to invoke each
* PHP Runnable implementations concurrently.
*/
public class PhpThreads {

    public static final String runnable = new String("<?php\n" +
    		"function run() {\n" +
    		" $out = java_context()->getAttribute('sharedResource', 100);\n" +
    		" $nr = (string)java_context()->getAttribute('nr', 100);\n" +
    		" echo \"started thread: $nr\n\";\n" +
    		" for($i=0; $i<100; $i++) {\n" +
    		"  $out->write(ord($nr));\n" +
    		"  java('java.lang.Thread')->sleep(1);\n" +
    		" }\n" +
    		"}\n" +
    		"?>\n");
    
    static final int THREAD_COUNT = 5;
    public static void main(String[] args) throws Exception {
	ScriptEngineManager manager = new ScriptEngineManager();
	Thread threads[] = new Thread[THREAD_COUNT];
	ScriptEngine engines[] = new ScriptEngine[THREAD_COUNT];
	ByteArrayOutputStream sharedResource = new ByteArrayOutputStream();
	StringReader runnableReader = new StringReader(runnable);

	// create THREAD_COUNT PHP threads
	for (int i=0; i<THREAD_COUNT; i++) {
	    engines[i] = manager.getEngineByName("php-invocable");
	    if (engines[i] == null)
		throw new NullPointerException ("php script engine not found");
	    
	    engines[i].put("nr", new Integer(i+1));
	    engines[i].put("sharedResource", sharedResource);
	    
	    engines[i].eval(runnableReader);
	    runnableReader.reset();

	    // cast the whole script to Runnable; note also getInterface(continuation, type)
	    Runnable r = (Runnable) ((Invocable)engines[i]).getInterface(Runnable.class);
	    threads[i] = new Thread(r);
	}
	
	// run the THREAD_COUNT PHP threads
	for (int i=0; i<THREAD_COUNT; i++) {
	    threads[i].start();
	}
	
	// wait for the THREAD_COUNT PHP threads to finish
	for (int i=0; i<THREAD_COUNT; i++) {
	    threads[i].join();
	    ((Closeable)engines[i]).close();
	}
	
	// print the output generated by the THREAD_COUNT concurrent threads
	String result = sharedResource.toString();
	System.out.println(result);
	
	// Check result
	Object res=manager.getEngineByName("php").eval(
		"<?php " +
		"exit((int)('10011002100310041005'!=" +
		"@system(\"echo -n "+result+"|sed 's/./&\\\n/g'|sort|uniq -c|tr -d ' \\\n'\")));" +
		"?>");
	
	System.exit(((Number)res).intValue());
    }
}
