/* 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.server.headlessclient; import java.awt.Dimension; import java.awt.print.PageFormat; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.rmi.RemoteException; import java.sql.Types; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.locks.ReentrantLock; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.swing.SwingUtilities; import org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.Session; import org.apache.wicket.protocol.http.WicketFilter; import org.mozilla.javascript.Scriptable; import com.servoy.j2db.ApplicationException; import com.servoy.j2db.FormController; import com.servoy.j2db.FormManager; import com.servoy.j2db.IBeanManager; import com.servoy.j2db.IDataRendererFactory; import com.servoy.j2db.IForm; import com.servoy.j2db.IFormController; import com.servoy.j2db.IFormManagerInternal; import com.servoy.j2db.ILAFManager; import com.servoy.j2db.IServiceProvider; import com.servoy.j2db.ISessionClient; import com.servoy.j2db.J2DBGlobals; import com.servoy.j2db.Messages; import com.servoy.j2db.RuntimeWindowManager; import com.servoy.j2db.component.ComponentFactory; import com.servoy.j2db.dataprocessing.BufferedDataSet; import com.servoy.j2db.dataprocessing.FoundSetManager; import com.servoy.j2db.dataprocessing.IDataSet; import com.servoy.j2db.dataprocessing.IFoundSetInternal; import com.servoy.j2db.dataprocessing.IRecordInternal; import com.servoy.j2db.dataprocessing.IUserClient; import com.servoy.j2db.dataprocessing.IValueList; import com.servoy.j2db.dataprocessing.RelatedValueList; import com.servoy.j2db.dataprocessing.SwingFoundSetFactory; import com.servoy.j2db.persistence.InfoChannel; import com.servoy.j2db.persistence.RepositoryException; import com.servoy.j2db.persistence.Solution; import com.servoy.j2db.persistence.SolutionMetaData; import com.servoy.j2db.persistence.ValueList; import com.servoy.j2db.plugins.IClientPluginAccess; import com.servoy.j2db.scripting.FormScope; import com.servoy.j2db.server.headlessclient.dataui.WebDataRendererFactory; import com.servoy.j2db.server.headlessclient.dataui.WebItemFactory; import com.servoy.j2db.server.shared.ApplicationServerRegistry; import com.servoy.j2db.server.shared.IApplicationServer; import com.servoy.j2db.server.shared.WebCredentials; import com.servoy.j2db.smart.dataui.DataRendererFactory; import com.servoy.j2db.smart.dataui.SwingItemFactory; import com.servoy.j2db.ui.IComponent; import com.servoy.j2db.ui.ItemFactory; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.Pair; import com.servoy.j2db.util.PersistHelper; import com.servoy.j2db.util.RendererParentWrapper; import com.servoy.j2db.util.ScopesUtils; import com.servoy.j2db.util.ServoyException; import com.servoy.j2db.util.ServoyScheduledExecutor; import com.servoy.j2db.util.Settings; import com.servoy.j2db.util.Utils; /** * A client which can be used in a jsp page or inside the org.apache.wicket framework as webclient * * @author jblok */ public class SessionClient extends AbstractApplication implements ISessionClient, HttpSessionActivationListener { protected transient IDataRendererFactory<org.apache.wicket.Component> dataRendererFactory; protected transient ItemFactory itemFactory; //just for the cases there is no org.apache.wicket running private static WebClientsApplication wicket_app = new WebClientsApplication(); private static Session wicket_session = null; protected transient HttpSession session; private transient InfoChannel outputChannel; private RuntimeWindowManager jsWindowManager; private final HashMap<String, String> defaultUserProperties = new HashMap<String, String>(); private volatile boolean shuttingDown = false; private transient volatile ServoyScheduledExecutor scheduledExecutorService; protected SessionClient(ServletRequest req, String uname, String pass, String method, Object[] methodArgs, String solution) throws Exception { this(req, new WebCredentials(uname, pass), method, methodArgs, solution); } protected SessionClient(ServletRequest req, WebCredentials credentials, String method, Object[] methodArgs, String solution) throws Exception { super(credentials); if (req instanceof HttpServletRequest) { session = ((HttpServletRequest)req).getSession(); } getClientInfo().setApplicationType(getApplicationType()); getClientInfo().setSolutionIntendedToBeLoaded(solution); IServiceProvider prev = testThreadLocals(); try { settings = Settings.getInstance(); ((Settings)settings).loadUserProperties(defaultUserProperties); this.preferredSolutionMethodNameToCall = method; this.preferredSolutionMethodArguments = methodArgs; setAjaxUsage(solution); enableAnchors(solution); if (req == null) { String str = getSettings().getProperty("locale.default"); //$NON-NLS-1$ Locale loc = PersistHelper.createLocale(str); if (loc != null) { locale = loc; } else { locale = Locale.getDefault(); } } else { locale = req.getLocale(); } guessLocaleCountryIfAbsent(); applicationSetup(); applicationInit(); applicationServerInit(); serverInit(); } catch (Exception e) { // if exception directly do a shutdown, so that this client doesn't hang. shutDown(true); throw e; } finally { unsetThreadLocals(prev); } } private void guessLocaleCountryIfAbsent() { // fix weird firefox issue that doesn't report a country if (locale != null && "".equals(locale.getCountry()) && locale.getLanguage() != null && locale.getLanguage().length() > 0) { Locale[] locales = Locale.getAvailableLocales(); if (locales != null) { for (Locale current : locales) { if (this.locale.getLanguage().equals(current.getLanguage()) && current.getCountry().length() != 0 && (current.getVariant() == null || current.getVariant().isEmpty())) { this.locale = current; break; } } } } } @Override public void clearLoginForm() { super.clearLoginForm(); loggedIn(); } @Override protected void loggedIn() { credentials.setPassword(""); //$NON-NLS-1$ credentials.setUserName(getClientInfo().getUserUid()); } @Override protected void applicationSetup() { super.applicationSetup(); jsWindowManager = createJSWindowManager(); } protected RuntimeWindowManager createJSWindowManager() { return new DummyRuntimeWindowManager(this); } public RuntimeWindowManager getRuntimeWindowManager() { return jsWindowManager; } public void loadSolution(String solutionName) throws RepositoryException { try { SolutionMetaData solutionMetaData = getApplicationServer().getSolutionDefinition(solutionName, getSolutionTypeFilter()); if (solutionMetaData == null) { throw new IllegalArgumentException(Messages.getString("servoy.exception.solutionNotFound", new Object[] { solutionName })); //$NON-NLS-1$ } loadSolution(solutionMetaData); } catch (RemoteException e) { throw new RepositoryException(e); } } public boolean closeSolution(boolean force) { return closeSolution(force, null); } @Override public boolean closeSolution(boolean force, Object[] args) { if (super.closeSolution(force, args)) { reinitializeDefaultProperties(); return true; } return false; } /** * We can define this here to allow all server based client to run every solution type, * while WebClient as exception uses SolutionLoader logic to load a SOLUTION|WEB_CLIENT_ONLY */ @Override protected int getSolutionTypeFilter() { return super.getSolutionTypeFilter() | SolutionMetaData.MODULE | SolutionMetaData.SMART_CLIENT_ONLY | SolutionMetaData.WEB_CLIENT_ONLY | SolutionMetaData.PRE_IMPORT_HOOK | SolutionMetaData.POST_IMPORT_HOOK; } @Override protected void loadSolution(SolutionMetaData solutionMeta) throws RepositoryException { IServiceProvider prev = testThreadLocals(); try { loadSolutionsAndModules(solutionMeta); setAjaxUsage(solutionMeta.getName()); enableAnchors(solutionMeta.getName()); getScriptEngine(); } finally { unsetThreadLocals(prev); } // Note that getSolution() may return null at this point if the security.closeSolution() or security.logout() was called in onSolutionOpen } @Override protected boolean registerClient(IUserClient uc) throws Exception { boolean registered = false; try { registered = super.registerClient(uc); // when registered is false, client is registered but with a trial license // access the server directly to mark the client as local ApplicationServerRegistry.get().setServerProcess(getClientID()); } catch (final ApplicationException e) { if ((e.getErrorCode() == ServoyException.NO_LICENSE) || (e.getErrorCode() == ServoyException.MAINTENANCE_MODE)) { shutDown(true); } throw e; } return registered; } /** * @see com.servoy.j2db.ClientState#shutDown(boolean) */ @Override public void shutDown(boolean force) { shuttingDown = true; IServiceProvider prev = null; try { prev = testThreadLocals(); super.shutDown(force); if (scheduledExecutorService != null) { scheduledExecutorService.shutdownNow(); scheduledExecutorService = null; } } catch (RuntimeException e) { Debug.error("shutdown error:", e); throw e; } finally { unsetThreadLocals(prev); shuttingDown = false; } } @Override public boolean isShutDown() { return shuttingDown || super.isShutDown(); } static void onDestroy() { try { if (wicket_app != null) { WebClientsApplication tmp = wicket_app; wicket_app = null; WicketFilter wicketFilter = tmp.getWicketFilter(); if (wicketFilter != null) { wicketFilter.destroy(); } if (Application.exists() && Application.get() == tmp) { Application.unset(); } if (Session.exists() && Session.get() == wicket_session) { Session.unset(); } } else { wicket_app = null; wicket_session = null; } } catch (Exception e) { Debug.error("on destroy", e); } } /** * This method sets the service provider to this if needed. Will return the previous provider that should be set back later. * * @return previously set service provider. */ protected IServiceProvider testThreadLocals() { if (wicket_app != null) { if (!Application.exists()) { Application.set(wicket_app); } if (ApplicationServerRegistry.get() != null) { if (!Session.exists()) { synchronized (wicket_app) { if (wicket_session == null) { wicket_app.fakeInit(); wicket_session = wicket_app.newSession(new EmptyRequest(), null); } } Session.set(wicket_session); } } } IServiceProvider provider = J2DBGlobals.getServiceProvider(); if (provider != this) { // if this happens it is a webclient in developer.. // and the provider is not set for this web client. so it must be set. J2DBGlobals.setServiceProvider(this); } return provider; } /** * @param solutionName */ private void setAjaxUsage(String solutionName) { boolean ajaxEnabledOnServer = Utils.getAsBoolean(settings.getProperty("servoy.webclient.useAjax", "true")); //$NON-NLS-1$ //$NON-NLS-2$ if (ajaxEnabledOnServer) { ajaxEnabledOnServer = Utils.getAsBoolean(settings.getProperty("servoy.webclient.useAjax." + solutionName, "true")); //$NON-NLS-1$ //$NON-NLS-2$ } boolean supportsAjax = true;//TODO: disable when simple (pda) broser is detected getRuntimeProperties().put("useAJAX", Boolean.toString(ajaxEnabledOnServer && supportsAjax)); //$NON-NLS-1$ } private void enableAnchors(String solutionName) { boolean anchorsEnabledOnServer = Utils.getAsBoolean(settings.getProperty("servoy.webclient.enableAnchors", "true")); //$NON-NLS-1$ //$NON-NLS-2$ if (anchorsEnabledOnServer) { anchorsEnabledOnServer = Utils.getAsBoolean(settings.getProperty("servoy.webclient.enableAnchors." + solutionName, "true")); //$NON-NLS-1$ //$NON-NLS-2$ } getRuntimeProperties().put("enableAnchors", Boolean.toString(anchorsEnabledOnServer)); //$NON-NLS-1$ } public void valueBound(HttpSessionBindingEvent e) { } public void valueUnbound(HttpSessionBindingEvent e) { try { shutDown(true); } catch (Exception e1) { Debug.error(e1); } } @Override protected void solutionLoaded(Solution s) { super.solutionLoaded(s); J2DBGlobals.firePropertyChange(this, "solution", null, getSolution()); //$NON-NLS-1$ } @Override protected void createFoundSetManager() { foundSetManager = new FoundSetManager(this, new SwingFoundSetFactory()); foundSetManager.init(); } @Override protected IFormManagerInternal createFormManager() { return new WebFormManager(this, new DummyMainContainer(this)); } //overridden ssl-rmi seems not to work localy @Override protected boolean startApplicationServerConnection() { try { applicationServer = ApplicationServerRegistry.getService(IApplicationServer.class); return true; } catch (Exception ex) { reportError(Messages.getString("servoy.client.error.finding.dataservice"), ex); //$NON-NLS-1$ return false; } } protected ILAFManager createLAFManager() { return ApplicationServerRegistry.get().getLafManager(); } protected IBeanManager createBeanManager() { return ApplicationServerRegistry.get().getBeanManager(); } /* * _______________________________________________________________________________ */ public synchronized Object executeMethod(String visibleFormName, String methodName, Object[] arguments) throws Exception { Object retval = null; IServiceProvider prev = testThreadLocals(); try { String formName = visibleFormName; if (formName == null && ((FormManager)getFormManager()).getCurrentForm() != null) { formName = ((FormManager)getFormManager()).getCurrentForm().getName(); } if (formName != null) { FormController fp = ((FormManager)getFormManager()).leaseFormPanel(formName); if (fp != null && fp.isFormVisible()) { return fp.executeFunction(methodName, arguments, true, null, false, null); } else { throw new IllegalStateException("Cannot call method on non visible form " + formName); //$NON-NLS-1$ } } else { throw new IllegalArgumentException("No current visible form specified"); //$NON-NLS-1$ } } catch (IllegalStateException e1) { throw e1; } catch (IllegalArgumentException e2) { throw e2; } catch (Exception e) { Debug.error(e); } finally { unsetThreadLocals(prev); } return retval; } public WebClientsApplication getFakeApplication() { synchronized (wicket_app) { if (wicket_session == null) { wicket_app.fakeInit(); wicket_session = wicket_app.newSession(new EmptyRequest(), null); } } return wicket_app; } protected void unsetThreadLocals(IServiceProvider prev) { if (J2DBGlobals.getServiceProvider() != prev) { if (Application.exists() && Application.get() == wicket_app) { Application.unset(); } if (Session.exists() && Session.get() == wicket_session) { // make sure the 2 thread locals are just empty lists. Session.get().getDirtyObjectsList().clear(); Session.get().getTouchedPages().clear(); Session.unset(); } J2DBGlobals.setServiceProvider(prev); } } public synchronized Object getDataProviderValue(String contextName, String dataProviderID) { if (dataProviderID == null) return null; IServiceProvider prev = testThreadLocals(); try { Object value = null; if (ScopesUtils.isVariableScope(dataProviderID)) { value = getScriptEngine().getSolutionScope().getScopesScope().get(null, dataProviderID); } else { Pair<IRecordInternal, FormScope> p = getContext(contextName); if (p != null) { FormScope fs = p.getRight(); IRecordInternal record = p.getLeft(); if (fs != null && fs.has(dataProviderID, fs)) // how can fs be null. { value = fs.get(dataProviderID); } else if (record != null) { value = record.getValue(dataProviderID); } if (value == Scriptable.NOT_FOUND) value = ""; //$NON-NLS-1$ } } return value; } finally { unsetThreadLocals(prev); } } public synchronized void saveData() { IServiceProvider prev = testThreadLocals(); try { getFoundSetManager().getEditRecordList().stopEditing(false); } finally { unsetThreadLocals(prev); } } private Pair<IRecordInternal, FormScope> getContext(String contextName) { try { String visibleFormName = contextName; String dataContext = null; if (contextName != null) { StringTokenizer tk = new StringTokenizer(contextName, "."); //$NON-NLS-1$ String token = tk.nextToken(); if (token.equals("forms") && tk.hasMoreTokens()) //$NON-NLS-1$ { visibleFormName = tk.nextToken(); if (tk.hasMoreTokens()) token = tk.nextToken(); } if (!token.equals("foundset")) //$NON-NLS-1$ { // todo why is this always also assigned to the datacontext? // shouldnt the above if be: if (token.equals("foundset") && st.hasMoreTokes()) dataContext == st.nextToken(); // because now this data context will just be set to a form name if the contextName is just a form. (which in many cases it is defined like that) dataContext = token; } } if (visibleFormName == null) { IForm tmp = ((FormManager)getFormManager()).getCurrentForm(); if (tmp != null) visibleFormName = tmp.getName(); } if (visibleFormName != null) { // just overwrite the above assignment again if the datacontext is really also the form, so it wont be used later on. if (Utils.stringSafeEquals(visibleFormName, dataContext)) { dataContext = null; } FormController fp = ((FormManager)getFormManager()).leaseFormPanel(visibleFormName); if (!fp.isShowingData()) { if (fp.wantEmptyFoundSet()) { if (fp.getFormModel() != null) fp.getFormModel().clear(); } else { fp.loadAllRecords(); } } IFoundSetInternal fs = fp.getFoundSet(); if (fs != null) { int idx = fs.getSelectedIndex(); if (idx < 0) idx = 0; IRecordInternal r = fs.getRecord(idx); if (r != null) { if (dataContext != null) { IFoundSetInternal rfs = r.getRelatedFoundSet(dataContext); // rfs can be null because dataContext can just be a anything see above if (rfs != null) { r = rfs.getRecord(rfs.getSelectedIndex()); } } return new Pair<IRecordInternal, FormScope>(r, fp.getFormScope()); } } return new Pair<IRecordInternal, FormScope>(null, fp.getFormScope()); } } catch (Exception e) { Debug.error(e); } return null; } public synchronized Object setDataProviderValue(String contextName, String dataprovider, Object value) { IServiceProvider prev = testThreadLocals(); try { Pair<IRecordInternal, FormScope> p = getContext(contextName); return setDataProviderValue(p, dataprovider, value); } finally { unsetThreadLocals(prev); } } private Object setDataProviderValue(Pair<IRecordInternal, FormScope> p, String dataProviderID, Object obj) { Object prevValue = null; Pair<String, String> scope = ScopesUtils.getVariableScope(dataProviderID); if (scope.getLeft() != null) { getScriptEngine().getScopesScope().getGlobalScope(scope.getLeft()).put(scope.getRight(), obj); } else if (p != null) { IRecordInternal record = p.getLeft(); FormScope fs = p.getRight(); if (fs.has(dataProviderID, fs)) { prevValue = fs.get(dataProviderID); fs.put(dataProviderID, obj); } else if (record != null && record.startEditing()) { try { prevValue = record.getValue(dataProviderID); record.setValue(dataProviderID, obj); } catch (IllegalArgumentException e) { Debug.trace(e); } } } return prevValue; } public synchronized int setDataProviderValues(String contextName, HttpServletRequest request_data) { int retval = 0; if (request_data.getCharacterEncoding() == null) { try { request_data.setCharacterEncoding(wicket_app.getRequestCycleSettings().getResponseRequestEncoding()); } catch (UnsupportedEncodingException e) { Debug.log(e); } } IServiceProvider prev = testThreadLocals(); try { Pair<IRecordInternal, FormScope> p = getContext(contextName); Enumeration< ? > e = request_data.getParameterNames(); while (e.hasMoreElements()) { String param = (String)e.nextElement(); Object val = request_data.getParameter(param); Object oldVal = setDataProviderValue(p, param, val); if (!(Utils.equalObjects(oldVal, val))) retval++; } return retval; } finally { unsetThreadLocals(prev); } } public synchronized boolean setMainForm(String formName) { IServiceProvider prev = testThreadLocals(); try { IFormController fp = ((FormManager)getFormManager()).showFormInMainPanel(formName); if (fp != null && fp.getName().equals(formName)) { return true; } else { Debug.trace("Form panel " + fp + " is (still) current main form"); //$NON-NLS-1$ //$NON-NLS-2$ throw new IllegalArgumentException("Form " + formName + " not found"); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (IllegalArgumentException e1) { throw e1; } catch (Exception e) { Debug.error(e); } finally { unsetThreadLocals(prev); } return false; } public synchronized IDataSet getValueListItems(String contextName, String valuelistName) { IServiceProvider prev = testThreadLocals(); try { ValueList vl = getFlattenedSolution().getValueList(valuelistName); if (vl != null) { // TODO should getValueListItems not specify type and format?? IValueList valuelist = ComponentFactory.getRealValueList(this, vl, true, Types.OTHER, null, null); if (valuelist instanceof RelatedValueList) { Pair<IRecordInternal, FormScope> p = getContext(contextName); if (p != null) { IRecordInternal r = p.getLeft(); if (r != null) { valuelist.fill(r); } } } if (valuelist != null) { ArrayList<Object[]> rows = new ArrayList<Object[]>(); for (int i = 0; i < valuelist.getSize(); i++) { rows.add(new Object[] { valuelist.getElementAt(i), valuelist.getRealElementAt(i) }); } return new BufferedDataSet(new String[] { "displayValue", "realValue" }, rows); //$NON-NLS-1$ //$NON-NLS-2$ } } else { throw new IllegalArgumentException("Valuelist with name " + valuelistName + " not found"); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (Exception e) { Debug.error(e); } finally { unsetThreadLocals(prev); } return null; } public boolean isValid() { return !isShutDown() && getClientInfo() != null; } /* * _______________________________________________________________________________ */ public boolean isEventDispatchThread() { return true; } private final ReentrantLock executing = new ReentrantLock(); protected boolean isExecutionLocked() { return executing.isLocked(); } // invoke later can't add it to a runnable or something. It is not the same thing as invokelater on // swing utilities where it still happens on the event thread but a bit later which can't be done in a web client. @Override protected void doInvokeLater(Runnable r) { invokeAndWait(r); } public void invokeAndWait(Runnable r) { IServiceProvider prev = testThreadLocals(); // We test here for printing, WebForm.processFppInAWTEventQueue(..) will call SwingUtilities.invokeAndWait() to print in awt thread. if (!SwingUtilities.isEventDispatchThread()) executing.lock(); try { r.run(); } finally { if (!SwingUtilities.isEventDispatchThread()) executing.unlock(); unsetThreadLocals(prev); } } public String getApplicationName() { return "Servoy Headless Client"; //$NON-NLS-1$ } public int getApplicationType() { return HEADLESS_CLIENT; } @Override public ScheduledExecutorService getScheduledExecutor() { if (scheduledExecutorService == null && !isShutDown()) { synchronized (J2DBGlobals.class) { if (scheduledExecutorService == null) { scheduledExecutorService = new ServoyScheduledExecutor(1, 4, 1) { private IServiceProvider prev; @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); prev = testThreadLocals(); } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); unsetThreadLocals(prev); } }; } } } return scheduledExecutorService; } @Override public void output(Object msg, int level) { super.output(msg, level); if (outputChannel != null) outputChannel.info(msg != null ? msg.toString() : "NULL", level); //$NON-NLS-1$ } public String getUserProperty(String a_name) { if (a_name == null) return null; CharSequence name = Utils.stringLimitLenght(a_name, 255); if (session != null) { return (String)session.getAttribute(Settings.USER + name); } else { return getDefaultUserProperties().get(a_name); } } public String[] getUserPropertyNames() { List<String> retval = new ArrayList<String>(); if (session != null) { Enumeration< ? > it = session.getAttributeNames(); while (it.hasMoreElements()) { String key = (String)it.nextElement(); if (key.startsWith(Settings.USER)) { retval.add(key); } } } for (String defaultUserPropertyKey : getDefaultUserProperties().keySet()) { if (retval.indexOf(defaultUserPropertyKey) == -1) { retval.add(defaultUserPropertyKey); } } return retval.toArray(new String[retval.size()]); } /** * Overwrite this method with an empty definition if the derived client doens't want user properties reset at solution close. */ protected void reinitializeDefaultProperties() { defaultUserProperties.clear(); ((Settings)settings).loadUserProperties(defaultUserProperties); } public void setUserProperty(String a_name, String value) { if (a_name == null) return; CharSequence name = Utils.stringLimitLenght(a_name, 255); if (session != null) { if (value == null) { session.removeAttribute(Settings.USER + name); } else { session.setAttribute(Settings.USER + name, Utils.stringLimitLenght(value, 255)); } } else { if (value == null) { getDefaultUserProperties().remove(a_name); // clear } else { getDefaultUserProperties().put(name.toString(), Utils.stringLimitLenght(value, 255).toString()); // clear } } } public Map<String, String> getDefaultUserProperties() { return defaultUserProperties; } public boolean putClientProperty(Object name, Object val) { return false; } public Object getClientProperty(Object name) { return null; } /** * @see com.servoy.j2db.ClientState#testClientRegistered(Object) */ @Override protected boolean testClientRegistered(Object exception) { if (exception instanceof ServoyException && ((ServoyException)exception).getErrorCode() == ServoyException.InternalCodes.CLIENT_NOT_REGISTERED) { if (session != null) { Debug.log("Client was not registered, invalidating the http session"); //$NON-NLS-1$ try { shutDown(true); } catch (Exception e) { Debug.trace("error calling shutdown in a client is not registered call", e); //$NON-NLS-1$ } try { session.invalidate(); } catch (Exception e) { Debug.trace("error calling session invalidate in a client is not registered call", e); //$NON-NLS-1$ } } return false; } return true; } public ItemFactory getItemFactory() { if (Utils.getAsBoolean(getRuntimeProperties().get("isPrinting"))) //$NON-NLS-1$ { return new SwingItemFactory(this);//needed to be able to print } if (itemFactory == null) { itemFactory = new WebItemFactory(this); } return itemFactory; } public IDataRendererFactory< ? > getDataRenderFactory() { if (Utils.getAsBoolean(getRuntimeProperties().get("isPrinting"))) //$NON-NLS-1$ { return new DataRendererFactory(); // needed to be able to print } if (dataRendererFactory == null) { dataRendererFactory = createDataRenderFactory(); } return dataRendererFactory; } protected WebDataRendererFactory createDataRenderFactory() { return new WebDataRendererFactory(); } private transient RendererParentWrapper printingRendererParent; public RendererParentWrapper getPrintingRendererParent() { if (printingRendererParent == null) { printingRendererParent = new RendererParentWrapper(); } return printingRendererParent; } public void setPageFormat(PageFormat pf) { pageFormat = pf; } private transient PageFormat pageFormat = new PageFormat(); public PageFormat getPageFormat() { return pageFormat; } @Override public IClientPluginAccess getPluginAccess() { return (IClientPluginAccess)super.getPluginAccess(); } public Dimension getScreenSize() { return new Dimension(-1, -1); } public boolean showURL(String url, String target, String target_options, int timeout, boolean onRootFrame) { //ignore return false; } public void setOutputChannel(InfoChannel channel) { this.outputChannel = channel; } public void looseFocus() { //nop } private void writeObject(ObjectOutputStream stream) throws IOException { //serialize is not implemented } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { //serialize is not implemented } private boolean isFormElementsEditableInFindMode = true; /* * @see com.servoy.j2db.IApplication#setFormElementsEditableInFindMode(boolean) */ public void setFormElementsEditableInFindMode(boolean editable) { isFormElementsEditableInFindMode = editable; } /* * @see com.servoy.j2db.IApplication#isFormElementsEditableInFindMode() */ public boolean isFormElementsEditableInFindMode() { return isFormElementsEditableInFindMode; } /* * (non-Javadoc) * * @see javax.servlet.http.HttpSessionActivationListener#sessionDidActivate(javax.servlet.http.HttpSessionEvent) */ @Override public void sessionDidActivate(HttpSessionEvent arg0) { } /* * (non-Javadoc) * * @see javax.servlet.http.HttpSessionActivationListener#sessionWillPassivate(javax.servlet.http.HttpSessionEvent) */ @Override public void sessionWillPassivate(HttpSessionEvent arg0) { shutDown(true); } @Override public String getFormNameFor(IComponent component) { if (component instanceof Component) { MarkupContainer parent = ((Component)component).getParent(); while (!(parent instanceof WebForm)) { parent = parent.getParent(); } return ((WebForm)parent).getController().getName(); } return ""; //$NON-NLS-1$ } }