This is going to be lengthy.
Disclaimer: this specifically is work code I inherited from a predecessor, so I don't know why certain parts are coded as such, only that they are. This is on top of me not fully understanding the intention, as well as some of the syntax used, hence this post. If possible, I rather not edit anything outside of the primary file causing the issue. (I've gotten enough replies asking why my code is written as such without answering the question that it kinda annoys me)
The base function of the program is to read from a SQL table and populate it to a screen. The user can then edit a couple of fields and update it; saving it to the database for future reference, or to create a new entry into the database.
From what I'm understanding:-
Both Table A and Table B, while different overall, have some similar columns that are used as reference to each other(not explicitly defined)
The program has been working fine usually, but when I changed the datatype for a specific column in Table B from int to bigint and access the edit screen of an existing data, I start getting an exception message and the function breaks(though the overall program still works).
The error message only appears in the browser console log, pointing to a line in the cache.html. This error persist in 2 separate browsers, both Chrome and (IE Mode) Edge.
Table A also has a similar column, declared as int and the stored procedures usually reference this in the queries, so I thought there might be some conversion issue, but even after converting the datatype for both columns in both tables to bigint, the error still persist.
After further checking, the issue only happens when the datatype for the column in Table B is changed from int to bigint. When I try to change the related column in Table A from from int to bigint to match(without changing the datatype for the column in Table B), there isn't any issue.
On top of Java, the codebase is also using GWT (2.4) to populate the screen, the (edited) related code includes:
Client-Side:
private StackTraceElement[] newResult;
protected void setNewStackTrace(StackTraceElement[] result){
Log.debug("Ex setNewStackTrace! " + result);
newResult = result;
}
protected void buildScreen() {
try {
GWT.setUncaughtExceptionHandler(new
GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
Log.debug("Ex caught!", e);
Log.debug("Ex getCause: ", e.getCause());
try { <Service>.Util.getInstance().getDeobfuscateStackTrace(e.getStackTrace(), new AsyncCallback<StackTraceElement[]>() {
public void onSuccess(StackTraceElement[] result) {
setNewStackTrace(result);
}
public void onFailure(Throwable caught) {
WidgetUtils.handleError(caught);
}
});
} catch (ApplicationException e1) {
// TODO Auto-generated catch block
Log.debug("Ex ApplicationException: ", e1);
e1.printStackTrace();
}
e.setStackTrace(newResult);
}
});
prepareGrid();
(...)
prepareMainButtons();
(...)
prepareFormBinding();
(...)
});
layout();
} catch (Exception e) {
AppsUtil.handleError(e);
}
}
(...)
private DbQuery QUERY = new DbQuery();
private PagingGridHelper<BaseModel> grid;
private String SELECTED_REPORT;
private Boolean LOADED = false;
private void prepareGrid() {
try {
QUERY.set_auditDbTable("<SQL Table>");
QUERY.setTableName("<Stored Procedure>");
List<ColumnConfig> columns = getColumns();
RpcProxy<PagingLoadResult<? extends ModelData>> proxy = new RpcProxy<PagingLoadResult<? extends ModelData>>() {
protected void load(Object loadConfig, AsyncCallback<PagingLoadResult<? extends ModelData>> callback) {
if (SELECTED_REPORT == null) {
if (LOADED)
callback.onSuccess(new BasePagingLoadResult<BaseModel>( new ArrayList<BaseModel>() ));
} else {
FilterPagingLoadConfig conf = (FilterPagingLoadConfig) loadConfig;
DbQueryService.Util.getInstance().selectPage(QUERY, conf, callback);
}
}
};
(...)
String columnData="";
for (int x=0; x<columns.size();x++){
columnData+=columns.get(x).getHeader() + ", ";
}
grid = new PagingGridHelper<BaseModel>(QUERY, columns, proxy, DOWNLOAD, false);
(...)
});
} catch (Exception e) {
AppsUtil.handleError(e);
}
}
private void prepareMainButtons() throws Exception {
(...)
EDIT = SWUtil.createButton(SWButton.EDIT);
EDIT.addSelectionListener(new SelectionListener<ButtonEvent>() {
public void componentSelected(ButtonEvent ce) {
(...)
if (grid.getSelectionModel().getSelection().size() == 1) {
(...)
switchMode(Mode.DataEdit, false);
// related to issue
formBinding.bind((BaseModel) grid.getSelectionModel().getSelection().get(0));
}
}
});
(...)
}
private void prepareFormBinding() {
try{
formBinding = new FormBinding(EditorForm, true);
formBinding.setStore(new ListStore<BaseModel>());
formBinding.addListener(Events.BeforeBind, new Listener<BindingEvent>() {
public void handleEvent(BindingEvent be) {
forceReset();
deselectAllCurrency();
EditorForm.reset();
(...)
}
});
formBinding.addListener(Events.Bind, new Listener<BindingEvent>() {
(...)
} catch (Exception e) {
e.printStackTrace();
}
}
});
formBinding.addListener(Events.UnBind, new Listener<BindingEvent>() {
(...)
}
catch(Exception e){
e.printStackTrace();
}
}
public PagingGridHelper(DbQuery dquery, List<ColumnConfig> uColumns, RpcProxy<PagingLoadResult<? extends ModelData>> uproxy, final BaseDownload download, boolean checkable) {
this.query = dquery;
this.columns = uColumns;
this.proxy = uproxy;
this.checkable = checkable;
try {
if (query == null)
query = new DbQuery();
(...)
columnModel = new ColumnModel(columns);
if (proxy == null) {
proxy = new RpcProxy<PagingLoadResult<? extends ModelData>>() {
protected void load(Object loadConfig, AsyncCallback<PagingLoadResult<? extends ModelData>> callback) {
FilterPagingLoadConfig conf = (FilterPagingLoadConfig) loadConfig;
DbQueryService.Util.getInstance().selectAsPage(query, conf, callback);
}
};
}
loadConfig = new BaseFilterPagingLoadConfig(0, pageSize);
loader = new BasePagingLoader<PagingLoadResult<D>>(proxy) {
protected Object newLoadConfig() {
return loadConfig;
}
};
loader.setRemoteSort(true);
loader.setReuseLoadConfig(true);
store = new ListStore<D>(loader);
store.addListener(Store.DataChanged, new Listener<StoreEvent<? extends ModelData>>() {
public void handleEvent(StoreEvent<? extends ModelData> be) {
for (ModelData d : store.getModels()) {
(...)
}
}
});
(...)
grid = new Grid<D>(store, columnModel);
(...)
grid.setLoadMask(true);
grid.setBorders(true);
(...)
grid.addListener(Events.Attach, new Listener<BaseEvent>() {
public void handleEvent(BaseEvent be) {
loader.load(0, pageSize);
}
});
} catch (Exception e) {
GWT.log(e.getMessage(), e);
}
}
Server-side:
@Override
public StackTraceElement[] getDeobfuscateStackTrace(StackTraceElement[] e) throws ApplicationException {
// TODO Auto-generated method stub
StackTraceDeobfuscator testDeobfuscator = new StackTraceDeobfuscator("war/WEB-INF/deploy/<project>/symbolMaps/");
String strongName = request.getHeader(RpcRequestBuilder.STRONG_NAME_HEADER);
return testDeobfuscator.deobfuscateStackTrace(e, strongName);
}
After a long while, I think I managed to pinpoint to the where of the issue:
formBinding.bind((BaseModel) grid.getSelectionModel().getSelection().get(0));
which seems to relate to this (to be precise, this part does not hit)
formBinding.addListener(Events.Bind, new Listener<BindingEvent>() {
@SuppressWarnings("rawtypes")
public void handleEvent(BindingEvent be) {
(...)
} catch (Exception e) {
e.printStackTrace();
}
}
But I still do not know the why, as well as how to solve it. Any ideas?
As mentioned, the only thing changed was the data type of a specific column in Table B. Else, the overall program is working as expected, in both Chrome and (IE Mode) Edge.
Edit 28/7/2023
Read through the GWT documentation, and trying to make sense of Exception thrown.
I got this as a result of the addition of the GWT.setUncaughtExceptionHandler
above:
Edit 4/8/2023 Update exception image after implementing <style>PRETTY</style>
in the pom.xml
.
Edit 7/8/2023 Update exception image after implementing <style>DETAILED</style>
in the pom.xml
.
As well as adding the following in my .gwt.xml file:
And the following to my web.xml
<servlet>
<servlet-name>remoteLogging</servlet-name>
<servlet-class>com.google.gwt.logging.server.RemoteLoggingServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>remoteLogging</servlet-name>
<url-pattern>/<project>/remote_logging</url-pattern>
</servlet-mapping>
As recommended in the comments, I went and checked in detail on the
selectAsPage
function.
After walking through the codes from it, it turns out the issue is related to one of the utility functions that handle and converts the results read from the db query into a more standardized java data.
public static <D extends BaseModel> void processRow(ResultSet rs, List<D> rows, D model) throws SQLException {
StringBuffer sb = new StringBuffer();
if (model == null)
model = (D) new BaseModel();
for (int i = 1 ; i <= rs.getMetaData().getColumnCount() ; i++) {
String column = rs.getMetaData().getColumnName(i);
if (rs.getMetaData().getColumnType(i) == Types.INTEGER) {
model.set(column, rs.getInt(i) );
}
else if (rs.getMetaData().getColumnType(i) == Types.BIGINT) {
model.set(column, rs.getBigDecimal(i).toBigInteger() );
}
else if(rs.getMetaData().getColumnType(i) == Types.NVARCHAR) {
model.set(column, rs.getString(i));
} else if (rs.getMetaData().getColumnType(i) == Types.TIMESTAMP) {
model.set(column, Misc.getTimestamp(rs, i) );
} else if (rs.getMetaData().getColumnType(i) == Types.FLOAT) {
model.set(column, new Double( rs.getFloat(i)) );
} else if (rs.getMetaData().getColumnType(i) == Types.DOUBLE) {
model.set(column, new Double( rs.getFloat(i)) );
} else if (rs.getMetaData().getColumnType(i) == Types.DECIMAL) {
Double d = new Double( rs.getDouble(i) );
model.set(column, d.doubleValue() );
} else if (rs.getMetaData().getColumnType(i) == Types.BIT) {
model.set(column, rs.getInt(i) );
} else {
model.set(column, rs.getString(i) );
}
sb.append(column + "=" + rs.getString(i) + "; type=" + rs.getMetaData().getColumnType(i));
}
rows.add( model );
Specifically, this section originally didnt exist.
else if (rs.getMetaData().getColumnType(i) == Types.BIGINT) {
model.set(column, rs.getBigDecimal(i).toBigInteger() );
}
Thus the code could not properly handle BigInt
values; as a result defaulting the value as String
, which will eventually lead to the aforementioned ClassCastException
issue.
Once I added this code chunk(and modified a few stuffs here and there to cater for it), the issue more or less disappeared.
This frustratingly took me way too long to pinpoint.