/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.plugins;
import java.awt.Component;
import java.awt.Window;
import java.net.URL;
import java.net.URLStreamHandler;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.concurrent.ScheduledExecutorService;
import javax.swing.JFrame;
import javax.swing.JMenu;
import org.mozilla.javascript.Function;
import com.servoy.j2db.ClientVersion;
import com.servoy.j2db.FlattenedSolution;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.IBeanManager;
import com.servoy.j2db.IFormController;
import com.servoy.j2db.IFormManager;
import com.servoy.j2db.J2DBGlobals;
import com.servoy.j2db.MediaURLStreamHandler;
import com.servoy.j2db.cmd.ICmdManager;
import com.servoy.j2db.component.ComponentFactory;
import com.servoy.j2db.dataprocessing.IDataServer;
import com.servoy.j2db.dataprocessing.IDatabaseManager;
import com.servoy.j2db.persistence.IServer;
import com.servoy.j2db.persistence.RepositoryException;
import com.servoy.j2db.persistence.ScriptVariable;
import com.servoy.j2db.persistence.Style;
import com.servoy.j2db.scripting.GlobalScope;
import com.servoy.j2db.scripting.IScriptSupport;
import com.servoy.j2db.scripting.RuntimeWindow;
import com.servoy.j2db.solutionmodel.ISolutionModel;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.ILogLevel;
import com.servoy.j2db.util.IStyleSheet;
import com.servoy.j2db.util.ITaskExecuter;
import com.servoy.j2db.util.Pair;
import com.servoy.j2db.util.ScopesUtils;
import com.servoy.j2db.util.ServoyException;
import com.servoy.j2db.util.ServoyScheduledExecutor;
import com.servoy.j2db.util.ThreadingRemoteInvocationHandler;
import com.servoy.j2db.util.toolbar.IToolbarPanel;
/**
* Interface Impl.
*
* @author jblok
*/
public class ClientPluginAccessProvider implements IClientPluginAccess
{
private final IApplication application;
private final boolean useThreadingInvocationHandler;
public ClientPluginAccessProvider(IApplication app)
{
application = app;
useThreadingInvocationHandler = Boolean.parseBoolean(application.getSettings().getProperty("servoy.plugins.services.threaded", "true")); //$NON-NLS-1$ //$NON-NLS-2$
}
public IDataServer getDataServer()
{
return application.getDataServer();
}
public void output(Object msg)
{
application.output(msg, ILogLevel.INFO);
}
public void output(Object msg, int level)
{
application.output(msg, level);
}
public Map<Object, Object> getRuntimeProperties()
{
return application.getRuntimeProperties();
}
/**
* @deprecated use getUserUID
*/
@Deprecated
public Object getUserID()
{
return getUserUID();
}
public Object getUserUID()
{
return application.getUserUID();
}
public String getClientID()
{
return application.getClientID();
}
@Deprecated
public String getTransactionID(String serverName)
{
try
{
return application.getFoundSetManager().getTransactionID(serverName);
}
catch (ServoyException e)
{
Debug.error(e);
}
return null;
}
public String getSolutionName()
{
if (application.getSolution() != null)
{
return application.getSolution().getName();
}
return null;
}
public IFormManager getFormManager()
{
return application.getFormManager();
}
public IDatabaseManager getDatabaseManager()
{
return application.getFoundSetManager();
}
public ICmdManager getCmdManager()
{
return application.getCmdManager();
}
public IBeanManager getBeanManager()
{
return application.getBeanManager();
}
public ISolutionModel getSolutionModel()
{
return application.getScriptEngine().getSolutionModifier();
}
public String getApplicationName()
{
return application.getApplicationName();
}
public Properties getSettings()
{
return application.getSettings();
}
/**
* @deprecated
*/
@Deprecated
public JMenu getImportMenu()
{
return null;
}
/**
* @deprecated
*/
@Deprecated
public JMenu getExportMenu()
{
return null;
}
/**
* @deprecated use getCurrentWindow if possible (gives the real current window)
*/
@Deprecated
public JFrame getMainApplicationFrame()
{
return null;
}
/**
* @deprecated
*/
@Deprecated
public Window getCurrentWindow()
{
return null;
}
/**
* @deprecated
*/
@Deprecated
public Window getWindow(String windowName)
{
return null;
}
/**
* @deprecated
*/
@Deprecated
public IToolbarPanel getToolbarPanel()
{
return null;
}
/**
* @deprecated
*/
@Deprecated
public ITaskExecuter getThreadPool()
{
return (ServoyScheduledExecutor)application.getScheduledExecutor();
}
public ScheduledExecutorService getExecutor()
{
return application.getScheduledExecutor();
}
public void reportError(Component parentComponent, String msg, Object detail)
{
application.reportError(msg, detail);
}
public void handleException(String msg, Exception e)
{
application.handleException(msg, e);
}
public void reportError(String msg, Object detail)
{
application.reportError(msg, detail);
}
public void reportWarningInStatus(String s)
{
application.reportWarningInStatus(s);
}
public void blockGUI(String reason)
{
application.blockGUI(reason);
}
public void releaseGUI()
{
application.releaseGUI();
}
/**
* @deprecated
*/
@Deprecated
public void registerURLStreamHandler(String protocolName, URLStreamHandler handler)
{
//nop
}
/**
* @see com.servoy.j2db.plugins.IClientPluginAccess#getMediaURLStreamHandler()
*/
public URLStreamHandler getMediaURLStreamHandler()
{
return new MediaURLStreamHandler(application);
}
/**
* Get a server interface by name.
*
* @param name of the server
* @return IServer
* @throws RemoteException
* @throws RepositoryException
*/
public IServer getServer(String name) throws RemoteException, RepositoryException
{
return application.getSolution().getServer(name);
}
/**
* Get all the defined server interfaces.
*
* @return IServer[] all the servers
* @throws RemoteException
* @throws RepositoryException
*/
public String[] getServerNames() throws RemoteException, RepositoryException
{
return application.getRepository().getServerNames(true);
}
/**
* @return IApplication
*/
public IApplication getApplication()
{
return application;
}
public URL getServerURL()
{
return application.getServerURL();
}
@Deprecated
public Remote getServerService(String name) throws Exception
{
return getRemoteService(name);
}
public Remote getRemoteService(String name) throws Exception
{
Remote remote = application.getServerService(name);
if (remote == null || !useThreadingInvocationHandler || application.isRunningRemote())
{
return remote;
}
return ThreadingRemoteInvocationHandler.createThreadingRemoteInvocationHandler(remote);
}
/*
* @see com.servoy.j2db.plugins.IClientPluginAccess#setStatusText(java.lang.String)
*/
public void setStatusText(String txt)
{
application.setStatusText(txt, null);
}
/**
* @deprecated
*/
@Deprecated
public void exportObject(Remote object) throws RemoteException
{
//nop
}
/**
* @deprecated
*/
@Deprecated
public String getMessage(String i18nKey)
{
return application.getI18NMessage(i18nKey);
}
/**
* @deprecated
*/
@Deprecated
public String getMessage(String i18nKey, Object[] arguments)
{
return getI18NMessage(i18nKey, arguments);
}
public String getI18NMessage(String i18nKey, Object[] arguments)
{
return application.getI18NMessage(i18nKey, arguments);
}
/**
* @deprecated
*/
@Deprecated
public final void executeMethod(final String formname, final String methodname, final Object[] arguments)
{
try
{
executeMethod(formname, methodname, arguments, true);
}
catch (Exception e)
{
application.reportError("Error executing global method " + methodname, e); //$NON-NLS-1$
Debug.error(e);
}
}
public Object executeMethod(String context, String methodname, Object[] arguments, final boolean async) throws Exception
{
return executeMethod(context, methodname, arguments, async, true);
}
public Object executeMethod(String context, String methodname, Object[] arguments, final boolean async, final boolean stopUIEditing) throws Exception
{
if (application.isSolutionLoaded())
{
final MethodExecutor method = new MethodExecutor(context, methodname, arguments, async, stopUIEditing);
// We first added to a thread. then called invokeLater in it.
// else you can't have batch processors that add the same (one time run) job at the end of the run.
// see case: 61486
// this doesn't matter to much for session client. Because the current thread is the schedulers thread
// and not the request thread.
// maybe invokeLater should fix this. But for a session client this is not possible if that call
// happens in the request method.
synchronized (method)
{
// When application != J2DBGlobals.getServiceProvider() the method is called from client a to another (headless) client b.
// Method execution has to be done in a separate thread to prevent mixing thread locals from client a and b.
// This happens when the webclient uses the headless client plugin to call a method in the HC in the server-side of the plugin,
// since this is all server-side code the HC call is executed in the same thread.
if (application.isEventDispatchThread() && !async && application == J2DBGlobals.getServiceProvider())
{
application.invokeAndWait(method);
Object retval = method.getRetval();
if (retval instanceof Exception)
{
throw (Exception)retval;
}
return retval;
}
else
{
application.getScheduledExecutor().execute(new Runnable()
{
public void run()
{
if (!async && !application.isEventDispatchThread())
{
// if not async and not event dispatch thread
// and the debugger is enabled, execute this directly.
if (application.getScriptEngine() instanceof IScriptSupport &&
((IScriptSupport)application.getScriptEngine()).isAWTSuspendedRunningScript())
{
method.run();
return;
}
}
if (async)
{
application.invokeLater(method);
}
else
{
application.invokeAndWait(method);
}
}
});
if (!async)
{
Object retval;
try
{
method.wait();
retval = method.getRetval();
}
catch (InterruptedException e)
{
retval = e;
Debug.error(e);
}
if (retval instanceof Exception)
{
throw (Exception)retval;
}
return retval;
}
}
}
}
return null;
}
private class MethodExecutor implements Runnable
{
private final String context;
private final String methodname;
private final Object[] arguments;
private Object retval;
private final boolean async;
private final boolean stopUIEditing;
public MethodExecutor(String context, String methodname, Object[] arguments, boolean async, boolean stopUIEditing)
{
if (methodname == null)
{
throw new IllegalArgumentException("null methodname");
}
if (context == null)
{
// derive context from name (scopes.myscope.mymethod)
Pair<String, String> variableScope = ScopesUtils.getVariableScope(methodname);
this.context = ScriptVariable.SCOPES_DOT_PREFIX + (variableScope.getLeft() == null ? ScriptVariable.GLOBAL_SCOPE : variableScope.getLeft());
this.methodname = variableScope.getRight();
}
else
{
this.context = context;
this.methodname = methodname;
}
this.arguments = arguments;
this.async = async;
this.stopUIEditing = stopUIEditing;
}
public Object getRetval()
{
return retval;
}
@SuppressWarnings("nls")
public void run()
{
try
{
//solution can be closed in the mean time.
if (application.isSolutionLoaded())
{
if (context.startsWith(ScriptVariable.SCOPES_DOT_PREFIX))
{
String scopename = context.substring(ScriptVariable.SCOPES_DOT_PREFIX.length());
GlobalScope gs = application.getScriptEngine().getScopesScope().getGlobalScope(scopename);
Object function = gs == null ? null : gs.get(methodname);
if (function instanceof Function)
{
try
{
if (stopUIEditing) application.getFoundSetManager().getEditRecordList().prepareForSave(false); // push the updated elements data in the Record.
retval = application.getScriptEngine().executeFunction((Function)function, gs, gs, arguments, true, true);
}
catch (Exception e)
{
retval = e;
if (async) application.handleException(
"Exception calling global method '" + methodname + "' with arguments " + Arrays.toString(arguments) +
" in async mode on solution " + getSolutionName(), e);
}
}
else
{
retval = new IllegalArgumentException(
"global methodname: " + methodname + " didnt resolve to a method in solution " + getSolutionName()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
else
{
try
{
IFormController fp = application.getFormManager().leaseFormPanel(context);
if (fp != null)
{
fp.initForJSUsage();
fp.setView(fp.getView());
fp.executeOnLoadMethod();
try
{
// make sure the foundset of this form is in an initialized state.
if (!fp.isShowingData()) fp.loadAllRecordsImpl(true);
}
catch (Exception ex)
{
Debug.error(ex);
application.handleException(application.getI18NMessage("servoy.formPanel.error.formData"), ex); //$NON-NLS-1$
}
retval = fp.executeFunction(methodname, arguments, stopUIEditing, null, true, null, true, false, true);
}
else
{
retval = new IllegalArgumentException("form: " + context + " didnt resolve to a form in solution " + getSolutionName()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
catch (Exception e)
{
retval = e;
if (async) application.handleException(
"Exception calling form method '" + methodname + "' with arguments " + Arrays.toString(arguments) + " on form '" + context +
"' in async mode on solution " + getSolutionName(), e);
}
}
}
}
finally
{
synchronized (this)
{
notify();
}
}
}
}
public int getApplicationType()
{
return application.getApplicationType();
}
/**
* @see com.servoy.j2db.plugins.IClientPluginAccess#isInDeveloper()
*/
public boolean isInDeveloper()
{
return application.isInDeveloper();
}
public int getPlatform()
{
return application.getClientPlatform();
}
public String getVersion()
{
return ClientVersion.getVersion();
}
public int getReleaseNumber()
{
return ClientVersion.getReleaseNumber();
}
public ResourceBundle getResourceBundle(Locale locale)
{
return application.getResourceBundle(locale);
}
public IPluginManager getPluginManager()
{
return application.getPluginManager();
}
/**
* @see com.servoy.j2db.plugins.IClientPluginAccess#showFileOpenDialog(com.servoy.j2db.plugins.IMediaUploadCallback, java.lang.String, boolean, java.lang.String[])
*/
public void showFileOpenDialog(IMediaUploadCallback callback, String fileNameHint, boolean multiSelect, String[] filter, int selection, String dialogTitle)
{
//nop
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.plugins.IClientPluginAccess#getStyleSheet(java.lang.String)
*/
public IStyleSheet getStyleSheet(String name)
{
FlattenedSolution flattenedSolution = application.getFlattenedSolution();
if (flattenedSolution == null)
{
return null; // when called before flattened solution is set.
}
String style_name = ComponentFactory.getOverriddenStyleName(application, name);
Style s = flattenedSolution.getStyle(style_name);
return ComponentFactory.getCSSStyle(application, s);
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.plugins.IClientPluginAccess#getRuntimeWindow(java.lang.String)
*/
public IRuntimeWindow getRuntimeWindow(String name)
{
return application.getRuntimeWindowManager().getWindow(name);
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.plugins.IClientPluginAccess#getCurrentRuntimeWindow()
*/
public IRuntimeWindow getCurrentRuntimeWindow()
{
RuntimeWindow windowImpl = application.getRuntimeWindowManager().getCurrentWindow();
if (windowImpl != null && windowImpl.getWrappedObject() != null)
{
return windowImpl;
}
return application.getRuntimeWindowManager().getWindow(null);
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.plugins.IClientPluginAccess#setValueListItems(java.lang.String, java.lang.Object[], java.lang.Object[])
*/
public void setValueListItems(String name, Object[] displayValues, Object[] realValues)
{
application.setValueListItems(name, displayValues, realValues, true);
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.plugins.IClientPluginAccess#setValueListItems(java.lang.String, java.lang.Object[])
*/
public void setValueListItems(String name, Object[] displayValues)
{
application.setValueListItems(name, displayValues, null, true);
}
@Override
public Map<String, String> getUserProperties()
{
Map<String, String> map = new HashMap<String, String>();
String[] userPropNames = application.getUserPropertyNames();
if (userPropNames != null)
{
for (String propName : userPropNames)
{
map.put(propName, application.getUserProperty(propName));
}
}
return map;
}
@Override
public void setUserProperties(Map<String, String> properties)
{
if (properties != null)
{
for (String propName : properties.keySet())
{
application.setUserProperty(propName, properties.get(propName));
}
}
}
}