/*
* Copyright (c) 2010-2011, Visage Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name Visage nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.visage.runtime;
import org.visage.animation.AnimationProvider;
import org.visage.animation.DefaultAnimationProvider;
import org.visage.functions.Function0;
import org.visage.runtime.sequence.Sequence;
import org.visage.runtime.sequence.Sequences;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Queue;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Entry point into Visage applications
*
* @author Stephen Chin <steveonjava@gmail.com>
*/
public class Entry {
private static RuntimeProvider runtimeProvider;
private static AnimationProvider animationProvider;
private static NamedArgumentProvider namedArgProvider;
private static String[] commandLineArgs;
public static void start(final Class<?> app, String[] args) throws Throwable {
if (args != null) {
CommandLineNamedArgumentProvider clnap = new CommandLineNamedArgumentProvider(args);
setNamedArgumentProvider(clnap);
commandLineArgs = clnap.getValues();
}
final Method main = app.getMethod(entryMethodName(), Sequence.class);
Object argSeq = Sequences.make(TypeInfo.String, args);
try {
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
@Override
public Void run() {
CodeSource codesource = app.getProtectionDomain().getCodeSource();
if (codesource != null) {
SystemProperties.setVisageProperty(SystemProperties.codebase, codesource.getLocation().toString());
}
main.setAccessible(true);
return null;
}
});
} catch (AccessControlException e) {
// security issue in applet or jnlp -- ignore
}
if (getRuntimeProvider().usesRuntimeLibrary(app)) {
getRuntimeProvider().run(main, args);
} else {
try {
main.invoke(null, argSeq);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
}
public static void deferAction(Runnable function) {
getRuntimeProvider().deferAction(function);
}
public static void deferAction(final Function0<Void> function) {
deferAction(new Runnable() {
@Override
public void run() {
function.invoke$(null, null, null);
}
});
}
public static void exit() {
getRuntimeProvider().exit();
}
private static RuntimeProvider getRuntimeProvider() {
if (runtimeProvider == null) {
try {
runtimeProvider = AccessController.doPrivileged(
new PrivilegedAction<RuntimeProvider>() {
@Override
public RuntimeProvider run() {
try {
return ServiceLoader.load(RuntimeProvider.class, Thread.currentThread().getContextClassLoader()).iterator().next();
} catch (ServiceConfigurationError e) {
} catch (NoSuchElementException e) {
}
return null;
}
});
} catch (Exception e) {
// ignore all exceptions and assign the runtime default (finally block)
} finally {
if (runtimeProvider == null) {
runtimeProvider = new NoRuntimeDefault();
}
}
}
return runtimeProvider;
}
public static AnimationProvider getAnimationProvider() {
if (animationProvider == null) {
try {
animationProvider = AccessController.doPrivileged(
new PrivilegedAction<AnimationProvider>() {
@Override
public AnimationProvider run() {
try {
return ServiceLoader.load(AnimationProvider.class, Thread.currentThread().getContextClassLoader()).iterator().next();
} catch (ServiceConfigurationError e) {
} catch (NoSuchElementException e) {
}
return null;
}
});
} finally {
if (animationProvider == null) {
animationProvider = new DefaultAnimationProvider();
}
}
}
return animationProvider;
}
public static void setNamedArgumentProvider(NamedArgumentProvider provider) {
namedArgProvider = provider;
}
public static Sequence<? extends String> getArguments() {
return Sequences.make(TypeInfo.String, commandLineArgs);
}
private static Object getArgument(int argument) {
if (commandLineArgs == null || argument < 0 || argument >= commandLineArgs.length) {
return null;
}
return commandLineArgs[argument];
}
public static Object getArgument(String key) {
if (namedArgProvider != null) {
Object value = namedArgProvider.get(key);
if (value != null) {
return value;
}
}
// treat as a numeric index
try {
return getArgument(Integer.parseInt(key));
} catch (NumberFormatException e) {
return null;
}
}
public static String entryMethodName() {
return "visage$run$";
}
private static class NoRuntimeDefault extends Thread implements RuntimeProvider {
private final Queue<Runnable> tasks = new ConcurrentLinkedQueue<Runnable>();
@Override
public boolean usesRuntimeLibrary(Class application) {
return true;
}
@Override
public Object run(final Method entryPoint, final String... args) throws Throwable {
/*
* Add the Script invokation to the Queue
*/
tasks.add(new Runnable() {
@Override
public void run() {
try {
main(entryPoint, args);
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
});
this.start();
return null;
}
private boolean hasActiveAnimation() {
AnimationProvider animationProvider = getAnimationProvider();
return animationProvider != null && animationProvider.hasActiveAnimation();
}
@Override
@SuppressWarnings("SleepWhileInLoop")
public void run() {
try {
while (!tasks.isEmpty() || hasActiveAnimation()) {
if (!tasks.isEmpty()) {
tasks.remove().run();
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
break;
}
}
}
visage.lang.Visage.exit(); // implicit exit after timeline is complete
} catch (VisageExit visagee) {
return; // trap VisageExit exception from bubbling up
}
}
private Object main(Method entryPoint, String... args) throws Throwable {
try {
return entryPoint.invoke(null, Sequences.make(TypeInfo.String, args));
} catch (InvocationTargetException ite) {
Throwable cause = ite.getCause();
if (cause instanceof VisageExit) { // explicit exit
return null;
}
throw cause;
}
}
@Override
public void deferAction(Runnable runnable) {
tasks.add(runnable);
}
@Override
public void exit() {
try {
System.exit(0);
} catch (SecurityException se) {
// ignore
}
}
}
private static class CommandLineNamedArgumentProvider implements NamedArgumentProvider {
private static final char SEPARATOR = '=';
private Properties namedArguments = new Properties();
private String[] values;
private CommandLineNamedArgumentProvider(String[] args) {
values = new String[args.length];
for (int i = 0; i < args.length; i++) {
String arg = args[i];
int index = arg.indexOf(SEPARATOR);
if (index > 0) {
String value = arg.substring(index + 1);
namedArguments.setProperty(arg.substring(0, index), value);
values[i] = value;
} else {
values[i] = arg;
}
}
}
@Override
public Object get(String name) {
return namedArguments.getProperty(name);
}
public String[] getValues() {
return values;
}
}
}