/*
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.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.annotations.JSFunction;
import com.servoy.j2db.Messages;
import com.servoy.j2db.dataprocessing.DataException;
import com.servoy.j2db.documentation.ServoyDocumented;
import com.servoy.j2db.scripting.IConstantsObject;
import com.servoy.j2db.scripting.IReturnedTypesProvider;
/**
* IMPORTANT: The names are exposed to javascripting do not refactor names!
* @author jblok
*/
@ServoyDocumented(category = ServoyDocumented.RUNTIME, publicName = "ServoyException", scriptingName = "ServoyException")
public class ServoyException extends Exception implements IReturnedTypesProvider, IConstantsObject
{
private static final long serialVersionUID = 3598145362930457281L;
// --------------------------------------------
//db set 1xx
// --------------------------------------------
/**
* Exception code for UNKNOWN_DATABASE_EXCEPTION.
*
* This code is used when an unrecognized database exception has occurred.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int UNKNOWN_DATABASE_EXCEPTION = 100;
/**
* Exception code for DATA_INTEGRITY_VIOLATION.
*
* This code is used when a database exception is recognized as an integrity exception (like constraint violation).
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int DATA_INTEGRITY_VIOLATION = 101;
/**
* Exception code for BAD_SQL_SYNTAX.
*
* This code is used when a database exception is recognized as an sql syntax error.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int BAD_SQL_SYNTAX = 102;
/**
* Exception code for PERMISSION_DENIED.
*
* This code is used when a database exception is recognized as a authorization error.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int PERMISSION_DENIED = 103;
/**
* Exception code for DEADLOCK.
*
* This code is used when a deadlock is detected by the database.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int DEADLOCK = 104;
/**
* Exception code for DATA_ACCESS_RESOURCE_FAILURE.
*
* This code is used when a database exception received an error accessing storage devices.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int DATA_ACCESS_RESOURCE_FAILURE = 105;
/**
* Exception code for ACQUIRE_LOCK_FAILURE.
*
* This code is used when a database failed to lock a row or table.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int ACQUIRE_LOCK_FAILURE = 106;
/**
* Exception code for INVALID_RESULTSET_ACCESS.
*
* This code is used when a data is requested that is not selected in the sql.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int INVALID_RESULTSET_ACCESS = 107;
/**
* Exception code for UNEXPECTED_UPDATE_COUNT.
*
* This code is used when a data could not be deleted or updated when expected (for example
* when a record was deleted outside Servoy and a Servoy client wants to update the record).
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int UNEXPECTED_UPDATE_COUNT = 108;
// --------------------------------------------
//application error code should be in 300 range
// --------------------------------------------
/**
* Exception code for NO_LICENSE.
*
* This code is used when a client could not be registered with the server because of license limitations.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_LICENSE = 307;
/**
* Exception code for RECORD_LOCKED.
*
* This code is used when a record could not be updated or deleted because it is locked by another client.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int RECORD_LOCKED = 308;
/**
* Exception code for INVALID_INPUT_FORMAT.
*
* This code is not used.
*
* @deprecated This code is not used
*/
@Deprecated
public static final int INVALID_INPUT_FORMAT = 309;
/**
* Exception code for INVALID_INPUT.
*
* This code is used when the user enters data that could not be validated.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int INVALID_INPUT = 310;
/**
* Exception code for EXECUTE_PROGRAM_FAILED.
*
* This code is used when an external program was not executed correctly.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int EXECUTE_PROGRAM_FAILED = 311;
/**
* Exception code for INCORRECT_LOGIN.
*
* This code is used when the user enters invalid credentials.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int INCORRECT_LOGIN = 312;
/**
* Exception code for NO_MODIFY_ACCESS.
*
* This code is used when a user wants to update data and this is disallowed by security settings.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_MODIFY_ACCESS = 319;
/**
* Exception code for NO_ACCESS.
*
* This code is used when a user wants to view data and this is disallowed by security settings.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_ACCESS = 320;
/**
* Exception code for NO_DELETE_ACCESS.
*
* This code is used when a user wants to delete data and this is disallowed by security settings.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_DELETE_ACCESS = 322;
/**
* Exception code for NO_CREATE_ACCESS.
*
* This code is used when a user wants to create new records and this is disallowed by security settings.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_CREATE_ACCESS = 323;
/**
* Exception code for NO_RELATED_CREATE_ACCESS.
*
* This code is used when a user wants to create new related records and this is disallowed by security settings.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_RELATED_CREATE_ACCESS = 324;
// public static final int VALIDATOR_NOT_FOUND = 327;
// public static final int CONVERTER_NOT_FOUND = 328;
/**
* Exception code for SAVE_FAILED.
*
* This code is used when a javascript exception occurred during saving data to the database.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int SAVE_FAILED = 330;
/**
* Exception code for NO_PARENT_DELETE_WITH_RELATED_RECORDS.
*
* This code is used when a record could not be deleted because a non-empty relation exists for the record that does not allow parent delete when having related records.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int NO_PARENT_DELETE_WITH_RELATED_RECORDS = 331;
/**
* Exception code for DELETE_NOT_GRANTED.
*
* This code is used when a record deletion was rejected by a pre-delete Servoy trigger.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int DELETE_NOT_GRANTED = 332;
/**
* Exception code for MAINTENANCE_MODE.
*
* This code is used when a client could not be registered with the server because the server is in maintenance mode.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int MAINTENANCE_MODE = 333;
/**
* Exception code for ABSTRACT_FORM.
*
* This code is used when a form, that cannot be created, is shown (for example, a form without parts).
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int ABSTRACT_FORM = 334;
/**
* Exception code for RECORD_VALIDATION_FAILED.
*
* This code is used when a record update/insert was rejected by a pre-update/insert Servoy trigger.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int RECORD_VALIDATION_FAILED = 335;
/**
* Exception code for CLIENT_NOT_AUTHORIZED.
*
* This code is used when an client performs an action that requires the user to be logged in and the user has not logged in yet.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
*/
public static final int CLIENT_NOT_AUTHORIZED = 336;
/**
* Error codes not available from java-script.
*/
public static class InternalCodes
{
public static final int UNKNOWN_EXCEPTION = 0;
public static final int INTERNAL_ERROR = 1;
// --------------------------------------------
//repository set 2xx
// --------------------------------------------
public static final int ERROR_NO_REPOSITORY_IN_DB = 204;
public static final int ERROR_OLD_REPOSITORY_IN_DB = 205;
public static final int ERROR_TOO_NEW_REPOSITORY_IN_DB = 206;
public static final int SERVER_NOT_FOUND = 213;
public static final int TABLE_NOT_FOUND = 214;
public static final int COLUMN_NOT_FOUND = 225;
public static final int PRIMARY_KEY_NOT_FOUND = 221;
public static final int ERROR_IN_TRANSACTION = 202;
public static final int NO_TRANSACTION_ACTIVE = 215;
public static final int INVALID_RMI_SERVER_CONNECTION = 216;
public static final int CUSTOM_REPOSITORY_ERROR = 217;
public static final int CHECKSUM_FAILURE = 218;
public static final int ELEMENT_CHANGED_TYPE = 219; // an element (fixed uuid) changed object type between revisions
public static final int INVALID_PROPERTY_VALUE = 220;
public static final int INVALID_EXPORT = 226;
public static final int CONNECTION_POOL_EXHAUSTED = 227;
// --------------------------------------------
//unknown set 4xx
// --------------------------------------------
public static final int CONNECTION_LOST = 401;
public static final int OPERATION_CANCELLED = 403;
public static final int JS_SCRIPT_ERROR = 410; //only use for js errors which halts the script
public static final int CLIENT_NOT_REGISTERED = 420;
}
private final int errorCode;
protected final Object[] tagValues;
private String scriptStackTrace;
public ServoyException()
{
this(0, null);
} // for scripting purposes
public ServoyException(int errorCode)
{
this(errorCode, null);
}
public ServoyException(int errorCode, Object[] values)
{
super();
this.errorCode = errorCode;
tagValues = values;
fillScriptStack();
}
/**
* Returns the errorCode.
*
* @return int
*/
public int getErrorCode()
{
return errorCode;
}
/**
* Always true; it makes the distinction between ServoyException and DataException.
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
* @return true.
* @deprecated Use "typeof" operator instead.
*/
@Deprecated
public boolean js_isServoyException()
{
return true;
}
@Override
public String getMessage()
{
switch (errorCode)
{
case InternalCodes.CONNECTION_LOST :
return Messages.getString("servoy.applicationException.connectionLost"); //$NON-NLS-1$
case RECORD_LOCKED :
return Messages.getString("servoy.foundSet.recordLocked"); //$NON-NLS-1$
case InternalCodes.JS_SCRIPT_ERROR :
return Messages.getString("servoy.applicationException.javascriptError"); //$NON-NLS-1$
case NO_LICENSE :
return Messages.getString("servoy.applicationException.noLicense"); //$NON-NLS-1$
case EXECUTE_PROGRAM_FAILED :
return Messages.getString("servoy.applicationException.execureProgramFailed"); //$NON-NLS-1$
case INCORRECT_LOGIN :
return Messages.getString("servoy.applicationException.incorrectLogin"); //$NON-NLS-1$
case InternalCodes.SERVER_NOT_FOUND :
return Messages.getString("servoy.exception.serverNotFound", tagValues); //$NON-NLS-1$
case InternalCodes.TABLE_NOT_FOUND :
return Messages.getString("servoy.sqlengine.error.tableMissing", tagValues); //$NON-NLS-1$
case InternalCodes.COLUMN_NOT_FOUND :
return Messages.getString("servoy.sqlengine.error.columnMissing", tagValues); //$NON-NLS-1$
case InternalCodes.PRIMARY_KEY_NOT_FOUND :
return Messages.getString("servoy.exception.primaryKeyNeeded", tagValues); //$NON-NLS-1$
case InternalCodes.NO_TRANSACTION_ACTIVE :
return Messages.getString("servoy.sqlengine.error.noTransactionActive", tagValues); //$NON-NLS-1$
case InternalCodes.INVALID_RMI_SERVER_CONNECTION :
return Messages.getString("servoy.exception.invalidServerConnection"); //$NON-NLS-1$
case InternalCodes.ERROR_NO_REPOSITORY_IN_DB :
return "No repository found in the database."; //$NON-NLS-1$
case InternalCodes.ERROR_OLD_REPOSITORY_IN_DB :
return "Old repository found in the database. Repository version: " + tagValues[0] + ", software version: " + tagValues[1] + //$NON-NLS-1$ //$NON-NLS-2$
". Upgrade the repository first."; //$NON-NLS-1$
case InternalCodes.ERROR_TOO_NEW_REPOSITORY_IN_DB :
return "Repository found in database too new for this software version. Repository version: " + tagValues[0] + ", software version: " + //$NON-NLS-1$ //$NON-NLS-2$
tagValues[1] + ". Upgrade Servoy first."; //$NON-NLS-1$
case InternalCodes.CHECKSUM_FAILURE :
return "Checksum failure"; //$NON-NLS-1$
case InternalCodes.INVALID_EXPORT :
return "Invalid export"; //$NON-NLS-1$
case InternalCodes.CONNECTION_POOL_EXHAUSTED :
return "Connection pool for server " + tagValues[0] + " exhausted"; //$NON-NLS-1$ //$NON-NLS-2$
case InternalCodes.ERROR_IN_TRANSACTION :
return "Error in transaction"; //$NON-NLS-1$
case InternalCodes.CUSTOM_REPOSITORY_ERROR :
return tagValues[0] != null ? tagValues[0].toString() : ""; //$NON-NLS-1$
case NO_MODIFY_ACCESS :
return Messages.getString("servoy.foundSet.error.noModifyAccess"); //$NON-NLS-1$
case NO_ACCESS :
return Messages.getString("servoy.foundSet.error.noAccess"); //$NON-NLS-1$
case NO_DELETE_ACCESS :
return Messages.getString("servoy.foundSet.error.noDeleteAccess"); //$NON-NLS-1$
case NO_CREATE_ACCESS :
return Messages.getString("servoy.foundSet.error.noCreateAccess"); //$NON-NLS-1$
case NO_RELATED_CREATE_ACCESS :
return Messages.getString("servoy.foundset.error.createRelatedRecordsNotAllowed", tagValues); //$NON-NLS-1$
case RECORD_VALIDATION_FAILED :
return Messages.getString("servoy.foundset.error.recordValidationFailed", tagValues); //$NON-NLS-1$
case SAVE_FAILED :
return Messages.getString("servoy.formPanel.error.saveFormData"); //$NON-NLS-1$
case NO_PARENT_DELETE_WITH_RELATED_RECORDS :
return Messages.getString("servoy.foundset.error.noParentDeleteWithRelatedrecords", tagValues); //$NON-NLS-1$
case DELETE_NOT_GRANTED :
return Messages.getString("servoy.foundset.error.deleteNotGranted"); //$NON-NLS-1$
case MAINTENANCE_MODE :
return Messages.getString("servoy.applicationException.maintenanceMode"); //$NON-NLS-1$
case ABSTRACT_FORM :
return Messages.getString("servoy.formPanel.error.cannotShowForm"); //$NON-NLS-1$
case InternalCodes.OPERATION_CANCELLED :
return "Operation cancelled"; //$NON-NLS-1$
case INVALID_INPUT_FORMAT :
return Messages.getString("servoy.applicationException.invalidInputFormat", tagValues); //$NON-NLS-1$
case INVALID_INPUT :
return Messages.getString("servoy.applicationException.invalidInput") + (getCause() != null ? ", " + getCause().getMessage() : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
case CLIENT_NOT_AUTHORIZED :
return Messages.getString("servoy.client.notAuthorized"); //$NON-NLS-1$
case InternalCodes.CLIENT_NOT_REGISTERED :
return Messages.getString("servoy.sqlengine.error.notRegistered"); //$NON-NLS-1$
case UNEXPECTED_UPDATE_COUNT :
return "Update/insert failed, unexpected nr of records affected: expected " + tagValues[0] + ", actual " + tagValues[1]; //$NON-NLS-1$ //$NON-NLS-2$
default :
{
if (errorCode == 0 && getCause() != null)
{
return super.getMessage();
}
else
{
return Messages.getString("servoy.applicationException.errorCode", new Object[] { new Integer(errorCode) }); //$NON-NLS-1$
}
}
}
}
public int findErrorCode()
{
if (errorCode > 0)
{
return errorCode;
}
if (getCause() instanceof ServoyException)
{
return ((ServoyException)getCause()).findErrorCode();
}
return 0;
}
public boolean hasErrorCode(int code)
{
if (errorCode == code)
{
return true;
}
if (getCause() instanceof ServoyException)
{
return ((ServoyException)getCause()).hasErrorCode(code);
}
return false;
}
/**
* Returns the error code for this ServoyException. Can be one of the constants declared in ServoyException.
*
* @sample
* //this sample script should be attached to onError method handler in the solution settings
* application.output('Exception Object: '+ex)
* application.output('MSG: '+ex.getMessage())
* if (ex instanceof ServoyException)
* {
* /** @type {ServoyException} */
* var servoyException = ex;
* application.output("is a ServoyException")
* application.output("Errorcode: "+servoyException.getErrorCode())
* var trace = "";
* if (ex.getScriptStackTrace) trace = servoyException.getScriptStackTrace();
* else if (servoyException.getStackTrace) trace = servoyException.getStackTrace();
* if (servoyException.getErrorCode() == ServoyException.SAVE_FAILED)
* {
* plugins.dialogs.showErrorDialog( 'Error', 'It seems you did not fill in a required field', 'OK');
* //Get the failed records after a save
* var array = databaseManager.getFailedRecords()
* for( var i = 0 ; i < array.length ; i++ )
* {
* var record = array[i];
* application.output(record.exception);
* if (record.exception instanceof DataException)
* {
* /** @type {DataException} */
* var dataException = record.exception;
* application.output('SQL: '+dataException.getSQL())
* application.output('SQLState: '+dataException.getSQLState())
* application.output('VendorErrorCode: '+dataException.getVendorErrorCode())
* }
* }
* return false
* }
* }
* //if returns false or no return, error is not reported to client; if returns true error is reported
* //by default error report means logging the error, in smart client an error dialog will also show up
* return true
*
* @return the error code for this ServoyException. Can be one of the constants declared in ServoyException.
*/
public int js_getErrorCode()
{
return errorCode;
}
/**
* Returns the string message for this ServoyException.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
* @return the string message for this ServoyException.
*/
public String js_getMessage()
{
return getMessage();
}
/**
* Returns the stack trace for this ServoyException.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
* @return the string stack trace for this ServoyException.
*/
public String js_getStackTrace()
{
Writer result = new StringWriter();
this.printStackTrace(new PrintWriter(result));
return result.toString();
}
/**
* Returns the script stack trace for this ServoyException if this could be created.
*
* @sampleas js_getErrorCode()
* @see #js_getErrorCode()
* @return the string stack trace for this ServoyException.
*/
@JSFunction
public String getScriptStackTrace()
{
return scriptStackTrace;
}
/**
* fills the script stack if not already generated if there is a current script context
*/
public void fillScriptStack()
{
if (scriptStackTrace == null && Context.getCurrentContext() != null)
{
try
{
EcmaError jsError = ScriptRuntime.constructError(getMessage(), getMessage());
scriptStackTrace = jsError.getScriptStackTrace();
}
catch (Exception e)
{
// just ignore
}
}
}
/**
* @see com.servoy.j2db.scripting.IScriptObject#getAllReturnedTypes()
*/
public Class< ? >[] getAllReturnedTypes()
{
return new Class[] { DataException.class };
}
/**
* @see java.lang.Throwable#toString()
*/
@Override
public String toString()
{
if (errorCode == 0)
{
return "ServoyException"; //$NON-NLS-1$
}
return super.toString();
}
}