diff --git a/api/src/org/labkey/api/data/CachedResultSetBuilder.java b/api/src/org/labkey/api/data/CachedResultSetBuilder.java index 4f9b1ddd8f1..ffb4afb3ab3 100644 --- a/api/src/org/labkey/api/data/CachedResultSetBuilder.java +++ b/api/src/org/labkey/api/data/CachedResultSetBuilder.java @@ -17,6 +17,7 @@ import org.labkey.api.collections.ResultSetRowMapFactory; import org.labkey.api.collections.RowMap; +import org.labkey.api.data.ResultSetMetaDataImpl.ColumnMetaData; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -39,9 +40,9 @@ public static FromResultSet create(ResultSet rs) return new FromResultSet(rs); } - public static FromListOfMaps create(List> maps) + public static FromListOfMaps create(List> maps, ResultSetMetaData md) { - return create(maps, maps.get(0).keySet()); + return new FromListOfMaps(maps, md); } /** @@ -49,15 +50,26 @@ public static FromListOfMaps create(List> maps) * maps may need to be case-insensitive. How do you tell? If the maps have data and the keys match the columnNames, * but the ResultSet rowMap values are all null. * @param maps List of row data, possibly case-insensitive maps - * @param columnNames Collection of column names + * @param columns Collection of ColumnInfos populated with name and JdbcType at a minimum * * TODO: A case-insensitive option for the builder, but there may be performance impact for very large result sets * if the implementation were simply to wrap each incoming map with CaseInsensitiveHashMap. For now, onus is on the * caller to provide case insensitive maps when necessary. */ - public static FromListOfMaps create(List> maps, Collection columnNames) + public static FromListOfMaps create(List> maps, Collection columns) { - return new FromListOfMaps(maps, columnNames); + ResultSetMetaDataImpl md = new ResultSetMetaDataImpl(columns.size()); + + for (ColumnInfo column : columns) + { + ColumnMetaData col = new ColumnMetaData(); + col.columnName = column.getColumnName(); + col.columnLabel = column.getColumnName(); + col.columnType = column.getJdbcType().sqlType; + md.addColumn(col); + } + + return create(maps, md); } public abstract C getThis(); @@ -133,15 +145,14 @@ public CachedResultSet build() throws SQLException public final static class FromListOfMaps extends CachedResultSetBuilder { private final List> _maps; - private final Collection _columnNames; + private final ResultSetMetaData _md; - private ResultSetMetaData _md = null; private boolean _isComplete = true; - private FromListOfMaps(List> maps, Collection columnNames) + private FromListOfMaps(List> maps, ResultSetMetaData md) { _maps = maps; - _columnNames = columnNames; + _md = md; } @Override @@ -150,12 +161,6 @@ public FromListOfMaps getThis() return this; } - public FromListOfMaps setMetaData(ResultSetMetaData md) - { - _md = md; - return this; - } - public FromListOfMaps setComplete(boolean complete) { _isComplete = complete; @@ -164,27 +169,10 @@ public FromListOfMaps setComplete(boolean complete) public CachedResultSet build() { - if (_md == null) - _md = createMetaData(_columnNames); - return new CachedResultSet(_md, convertToRowMaps(_md, _maps), _isComplete, _requireClose, _stackTrace); } } - private static ResultSetMetaData createMetaData(Collection columnNames) - { - ResultSetMetaDataImpl md = new ResultSetMetaDataImpl(columnNames.size()); - for (String columnName : columnNames) - { - ResultSetMetaDataImpl.ColumnMetaData col = new ResultSetMetaDataImpl.ColumnMetaData(); - col.columnName = columnName; - col.columnLabel = columnName; - md.addColumn(col); - } - - return md; - } - private static ArrayList> convertToRowMaps(ResultSetMetaData md, List> maps) { ArrayList> list = new ArrayList<>(); diff --git a/api/src/org/labkey/api/data/DataRegion.java b/api/src/org/labkey/api/data/DataRegion.java index c67e71efb13..94930e050d5 100644 --- a/api/src/org/labkey/api/data/DataRegion.java +++ b/api/src/org/labkey/api/data/DataRegion.java @@ -2593,19 +2593,6 @@ private void renderOldValues(HtmlWriter out, Map valueMap, Map colInfosFromMetaData(ResultSetMetaData md) throws SQLException - { - int columnCount = md.getColumnCount(); - List cols = new LinkedList<>(); - - for (int i = 1; i <= columnCount; i++) - cols.add(new BaseColumnInfo(md, i)); - - return cols; - } - - /** * Render the data region. All rendering SHOULD go through this function * public renderForm, renderTable methods actually all go through here diff --git a/api/src/org/labkey/api/view/Portal.java b/api/src/org/labkey/api/view/Portal.java index 1ef5ddcee4c..5d57abeec9c 100644 --- a/api/src/org/labkey/api/view/Portal.java +++ b/api/src/org/labkey/api/view/Portal.java @@ -121,7 +121,7 @@ */ public class Portal implements ModuleChangeListener { - private static final Logger LOG = LogHelper.getLogger(Portal.class, "Page and web part warnings"); + private static final Logger LOG = LogHelper.getLogger(Portal.class, "Page and web part warnings and exceptions"); private static final WebPartBeanLoader FACTORY = new WebPartBeanLoader(); public static final String FOLDER_PORTAL_PAGE = "folder"; @@ -1714,6 +1714,7 @@ public static WebPartView getWebPartViewSafe(@NotNull WebPartFactory factory, @N WebPartView errorView; if (t instanceof HttpStatusException) { + LOG.debug("Exception while rendering a webpart", t); BindException errors = new BindException(new Object(), "form"); errors.reject(SpringActionController.ERROR_MSG, t.getMessage()); errorView = new SimpleErrorView(errors, false); diff --git a/pipeline/src/org/labkey/pipeline/analysis/ProtocolManagementWebPart.java b/pipeline/src/org/labkey/pipeline/analysis/ProtocolManagementWebPart.java index ec11a4039a7..80a7bfa5ce0 100644 --- a/pipeline/src/org/labkey/pipeline/analysis/ProtocolManagementWebPart.java +++ b/pipeline/src/org/labkey/pipeline/analysis/ProtocolManagementWebPart.java @@ -21,9 +21,9 @@ import org.labkey.api.data.CachedResultSetBuilder; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.DataRegion; +import org.labkey.api.data.JdbcType; import org.labkey.api.data.RenderContext; import org.labkey.api.data.ResultsImpl; -import org.labkey.api.data.RuntimeSQLException; import org.labkey.api.data.SimpleDisplayColumn; import org.labkey.api.pipeline.PipeRoot; import org.labkey.api.pipeline.PipelineJobService; @@ -42,7 +42,6 @@ import org.springframework.validation.BindException; import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -71,7 +70,6 @@ public ProtocolManagementWebPart(ViewContext viewContext) super(new DataRegion(), (BindException) null); setViewContext(viewContext); setTitle(NAME); - createResults(); getDataRegion().setSettings(new QuerySettings(getViewContext(), NAME)); getDataRegion().setSortable(false); getDataRegion().setShowFilters(false); @@ -81,6 +79,9 @@ public ProtocolManagementWebPart(ViewContext viewContext) getDataRegion().setShowRecordSelectors(true); } getDataRegion().setRecordSelectorValueColumns("taskId", "name"); + + // Create results last since some statements above could throw on bad input, causing us to abandon an unclosed Results + createResults(); } private ButtonBar createButtonBar() @@ -101,27 +102,24 @@ private ButtonBar createButtonBar() private void createResults() // Accept filter & sort ? Tough to use standard UI components the way this is wired in. { List> rows = getProtocols().stream().map(Protocol::toMap).collect(Collectors.toList()); - ResultSet rs = CachedResultSetBuilder.create(rows, Arrays.asList("taskId", "name", "pipeline", "archived")).build(); - try - { - List colInfos = DataRegion.colInfosFromMetaData(rs.getMetaData()); - - Map params = new HashMap<>(); - params.put("taskId", "taskId"); - params.put("name", "name"); - params.put("archived", "archived"); - ActionURL actionURL = new ActionURL(AnalysisController.ProtocolDetailsAction.class, getViewContext().getContainer()); - DetailsURL url = new DetailsURL(actionURL.addReturnUrl(getContextURLHelper()), params); - ((BaseColumnInfo)colInfos.get(1)).setURL(url); - setResults(new ResultsImpl(rs, colInfos)); - getDataRegion().setColumns(colInfos); - getDataRegion().getDisplayColumn("taskId").setVisible(false); - getDataRegion().replaceDisplayColumn("archived", new ArchivedDisplayColumn()); - } - catch (SQLException e) - { - throw new RuntimeSQLException(e); - } + List colInfos = List.of( + new BaseColumnInfo("taskId", JdbcType.VARCHAR), + new BaseColumnInfo("name", JdbcType.VARCHAR), + new BaseColumnInfo("pipeline", JdbcType.VARCHAR), + new BaseColumnInfo("archived", JdbcType.BOOLEAN) + ); + ResultSet rs = CachedResultSetBuilder.create(rows, colInfos).build(); + Map params = new HashMap<>(); + params.put("taskId", "taskId"); + params.put("name", "name"); + params.put("archived", "archived"); + ActionURL actionURL = new ActionURL(AnalysisController.ProtocolDetailsAction.class, getViewContext().getContainer()); + DetailsURL url = new DetailsURL(actionURL.addReturnUrl(getContextURLHelper()), params); + ((BaseColumnInfo)colInfos.get(1)).setURL(url); + setResults(new ResultsImpl(rs, colInfos)); + getDataRegion().setColumns(colInfos); + getDataRegion().getDisplayColumn("taskId").setVisible(false); + getDataRegion().replaceDisplayColumn("archived", new ArchivedDisplayColumn()); } private List getProtocols()