/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.management.internal.cli.result; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.zip.DataFormatException; import org.apache.geode.management.cli.Result; import org.apache.geode.management.internal.cli.GfshParser; import org.apache.geode.management.internal.cli.LogWrapper; import org.apache.geode.management.internal.cli.json.GfJsonArray; import org.apache.geode.management.internal.cli.json.GfJsonException; import org.apache.geode.management.internal.cli.json.GfJsonObject; import org.apache.geode.management.internal.cli.result.TableBuilder.Row; import org.apache.geode.management.internal.cli.result.TableBuilder.RowGroup; import org.apache.geode.management.internal.cli.result.TableBuilder.Table; /** * Wraps the Result of a command execution. * * @since GemFire 7.0 */ // Should this have info about the command String?? public class CommandResult implements Result { private GfJsonObject gfJsonObject; private Status status; private int index; private boolean isDataBuilt; private ResultData resultData; private List<String> resultLines; private boolean failedToPersist = false; private transient int numTimesSaved; public CommandResult(ResultData resultData) { this.resultData = resultData; this.gfJsonObject = this.resultData.getGfJsonObject(); this.status = this.resultData.getStatus(); this.resultLines = new Vector<String>(); } @Override public Status getStatus() { return this.status; } public ResultData getResultData() { return ResultBuilder.getReadOnlyResultData(resultData); } GfJsonObject getGfJsonObject() { return gfJsonObject; } @Override public void resetToFirstLine() { index = 0; } // TODO -Abhishek - extract this code out in a FormatBuilder or PresentationBuilder?? private void buildData() { try { if (ResultData.TYPE_OBJECT.equals(resultData.getType())) { buildObjectResultOutput(); } else if (ResultData.TYPE_COMPOSITE.equals(resultData.getType())) { buildComposite(); } else { GfJsonObject content = getContent(); if (content != null) { Table resultTable = TableBuilder.newTable(); addHeaderInTable(resultTable, getGfJsonObject()); RowGroup rowGroup = resultTable.newRowGroup(); if (ResultData.TYPE_TABULAR.equals(resultData.getType())) { // resultTable.setColumnSeparator(" | "); resultTable.setColumnSeparator(" "); resultTable.setTabularResult(true); buildTable(rowGroup, content); } else { buildInfoErrorData(rowGroup, content); } addFooterInTable(resultTable, getGfJsonObject()); resultLines.addAll(resultTable.buildTableList()); } } } catch (GfJsonException e) { resultLines .add("Error occurred while processing Command Result. Internal Error - Invalid Result."); // TODO - Abhishek. Add stack trace when 'debug' is enabled. Log to LogWrapper always } finally { isDataBuilt = true; } } private void addHeaderInTable(Table resultTable, GfJsonObject fromJsonObject) { String header = getHeader(fromJsonObject); if (header != null && !header.isEmpty()) { resultTable.newRow().newLeftCol(header); } } private void addHeaderInRowGroup(RowGroup rowGroup, GfJsonObject fromJsonObject) { String header = getHeader(fromJsonObject); if (header != null && !header.isEmpty()) { rowGroup.newRow().newLeftCol(header); } } private void addFooterInTable(Table resultTable, GfJsonObject fromJsonObject) { String footer = getFooter(fromJsonObject); if (footer != null && !footer.isEmpty()) { resultTable.newRow().newLeftCol(footer); } } private void addFooterInRowGroup(RowGroup rowGroup, GfJsonObject fromJsonObject) { String footer = getFooter(fromJsonObject); if (footer != null && !footer.isEmpty()) { rowGroup.newRow().newLeftCol(footer); } } private void buildInfoErrorData(RowGroup rowGroup, GfJsonObject content) throws GfJsonException { GfJsonArray accumulatedData = content.getJSONArray(InfoResultData.RESULT_CONTENT_MESSAGE); if (accumulatedData != null) { buildRows(rowGroup, null, accumulatedData); } } /* private */ void buildObjectResultOutput() { try { Table resultTable = TableBuilder.newTable(); resultTable.setColumnSeparator(" : "); addHeaderInTable(resultTable, getGfJsonObject()); GfJsonObject content = getContent(); GfJsonArray objectsArray = content.getJSONArray(ObjectResultData.OBJECTS_ACCESSOR); if (objectsArray != null) { int numOfObjects = objectsArray.size(); for (int i = 0; i < numOfObjects; i++) { GfJsonObject object = objectsArray.getJSONObject(i); buildObjectSection(resultTable, null, object, 0); } } /* * else { // GfJsonObject jsonObject = * content.getJSONObject(ObjectResultData.ROOT_OBJECT_ACCESSOR); // * buildObjectSection(resultTable, null, jsonObject, 0); } */ addFooterInTable(resultTable, getGfJsonObject()); resultLines.addAll(resultTable.buildTableList()); } catch (GfJsonException e) { resultLines .add("Error occurred while processing Command Result. Internal Error - Invalid Result."); // TODO - Abhishek. Add stack trace when 'debug' is enabled. Log to LogWrapper always } finally { isDataBuilt = true; } } private void buildObjectSection(Table table, RowGroup parentRowGroup, GfJsonObject object, int depth) throws GfJsonException { Iterator<String> keys = object.keys(); RowGroup rowGroup = null; if (parentRowGroup != null) { rowGroup = parentRowGroup; } else { rowGroup = table.newRowGroup(); } GfJsonArray nestedCollection = null; GfJsonObject nestedObject = null; GfJsonObject fieldDisplayNames = object.getJSONObject(CliJsonSerializable.FIELDS_TO_DISPLAYNAME_MAPPING); List<String> fieldsToSkipOnUI = null; if (object.has(CliJsonSerializable.FIELDS_TO_SKIP_ON_UI)) { GfJsonArray jsonArray = object.getJSONArray(CliJsonSerializable.FIELDS_TO_SKIP_ON_UI);; fieldsToSkipOnUI = new ArrayList<String>(); for (int i = 0; i < jsonArray.size(); i++) { fieldsToSkipOnUI.add(String.valueOf(jsonArray.get(i))); } } while (keys.hasNext()) { String key = keys.next(); if (CliJsonSerializable.FIELDS_TO_SKIP.contains(key) || (fieldsToSkipOnUI != null && fieldsToSkipOnUI.contains(key))) { continue; } try { nestedCollection = object.getJSONArray(key); } catch (GfJsonException e) { /* next check if it's a nested object */} Object field = null; if (nestedCollection == null) { field = object.get(key); if (!isPrimitiveOrStringOrWrapper(field)) { nestedObject = object.getJSONObject(key); } } if (nestedCollection != null && isPrimitiveOrStringOrWrapperArray(nestedCollection)) { String str = nestedCollection.toString(); field = str.substring(1, str.length() - 1); nestedCollection = null; } Row newRow = rowGroup.newRow(); String prefix = ""; /* if (nestedCollection != null) */ { for (int i = 0; i < depth; i++) { prefix += " . "; } } String fieldNameToDisplay = fieldDisplayNames.getString(key); if (nestedCollection == null) { newRow.newLeftCol(prefix + fieldNameToDisplay); } if (nestedCollection != null) { Map<String, Integer> columnsMap = new HashMap<String, Integer>(); GfJsonArray rowsArray = nestedCollection; RowGroup newRowGroup = table.newRowGroup(); newRowGroup.setColumnSeparator(" | "); newRowGroup.newBlankRow(); newRowGroup.newRow().newLeftCol(fieldNameToDisplay); Row headerRow = newRowGroup.newRow(); int numOfRows = rowsArray.size(); List<String> tableFieldsToSkipOnUI = null; for (int j = 0; j < numOfRows; j++) { GfJsonObject content = rowsArray.getJSONObject(j); if (content.has(CliJsonSerializable.FIELDS_TO_SKIP_ON_UI)) { GfJsonArray jsonArray = content.getJSONArray(CliJsonSerializable.FIELDS_TO_SKIP_ON_UI); tableFieldsToSkipOnUI = new ArrayList<String>(); for (int i = 0; i < jsonArray.size(); i++) { tableFieldsToSkipOnUI.add(String.valueOf(jsonArray.get(i))); } } GfJsonArray columnNames = content.names(); int numOfColumns = columnNames.size(); if (headerRow.isEmpty()) { GfJsonObject innerFieldDisplayNames = content.getJSONObject(CliJsonSerializable.FIELDS_TO_DISPLAYNAME_MAPPING); for (int i = 0; i < numOfColumns; i++) { Object columnName = columnNames.get(i); if (CliJsonSerializable.FIELDS_TO_SKIP.contains((String) columnName) || (tableFieldsToSkipOnUI != null && tableFieldsToSkipOnUI.contains(columnName))) { // skip file data if any //TODO - make response format better continue; } headerRow.newCenterCol(innerFieldDisplayNames.getString((String) columnName)); columnsMap.put((String) columnName, i); } newRowGroup.newRowSeparator('-', false); } newRow = newRowGroup.newRow(); for (int i = 0; i < numOfColumns; i++) { Object columnName = columnNames.get(i); if (CliJsonSerializable.FIELDS_TO_SKIP.contains((String) columnName) || (tableFieldsToSkipOnUI != null && tableFieldsToSkipOnUI.contains(columnName))) { // skip file data if any //TODO - make response format better continue; } newRow.newLeftCol(String.valueOf(content.get((String) columnName))); } } } else if (nestedObject != null) { buildObjectSection(table, rowGroup, nestedObject, depth + 1); } else { // Row newRow = rowGroup.newRow(); // String prefix = ""; // for (int i = 0; i < depth; i++) { // prefix += " . "; // } // newRow.newLeftCol(prefix+fieldDisplayNames.getString(key)).newLeftCol(field); Object value = field; /* * if (isPrimitiveOrStringOrWrapperArray(value)) { value = Arrays.toString((String[])value); * } */ newRow.newLeftCol(value); } nestedCollection = null; nestedObject = null; } } private boolean isPrimitiveOrStringOrWrapper(Object object) { boolean isPrimitive = false; if (String.class.isInstance(object)) { isPrimitive = true; } else if (byte.class.isInstance(object) || Byte.class.isInstance(object)) { isPrimitive = true; } else if (short.class.isInstance(object) || Short.class.isInstance(object)) { isPrimitive = true; } else if (int.class.isInstance(object) || Integer.class.isInstance(object)) { isPrimitive = true; } else if (long.class.isInstance(object) || Long.class.isInstance(object)) { isPrimitive = true; } else if (float.class.isInstance(object) || Float.class.isInstance(object)) { isPrimitive = true; } else if (double.class.isInstance(object) || Double.class.isInstance(object)) { isPrimitive = true; } else if (boolean.class.isInstance(object) || Boolean.class.isInstance(object)) { isPrimitive = true; } else if (char.class.isInstance(object) || Character.class.isInstance(object)) { isPrimitive = true; } return isPrimitive; } private boolean isPrimitiveOrStringOrWrapperArray(Object object) { boolean isPrimitive = false; if (String[].class.isInstance(object)) { isPrimitive = true; } else if (byte[].class.isInstance(object) || Byte[].class.isInstance(object)) { isPrimitive = true; } else if (short[].class.isInstance(object) || Short[].class.isInstance(object)) { isPrimitive = true; } else if (int[].class.isInstance(object) || Integer[].class.isInstance(object)) { isPrimitive = true; } else if (long[].class.isInstance(object) || Long[].class.isInstance(object)) { isPrimitive = true; } else if (float[].class.isInstance(object) || Float[].class.isInstance(object)) { isPrimitive = true; } else if (double[].class.isInstance(object) || Double[].class.isInstance(object)) { isPrimitive = true; } else if (boolean[].class.isInstance(object) || Boolean[].class.isInstance(object)) { isPrimitive = true; } else if (char[].class.isInstance(object) || Character[].class.isInstance(object)) { isPrimitive = true; } else if (GfJsonArray.class.isInstance(object)) { GfJsonArray jsonArr = (GfJsonArray) object; try { isPrimitive = isPrimitiveOrStringOrWrapper(jsonArr.get(0)); } catch (GfJsonException e) { } } return isPrimitive; } /* private */ void buildComposite() { try { GfJsonObject content = getContent(); if (content != null) { Table resultTable = TableBuilder.newTable(); resultTable.setColumnSeparator(" : "); addHeaderInTable(resultTable, getGfJsonObject()); for (Iterator<String> it = content.keys(); it.hasNext();) { String key = it.next(); if (key.startsWith(CompositeResultData.SECTION_DATA_ACCESSOR)) { GfJsonObject subSection = content.getJSONObject(key); buildSection(resultTable, null, subSection, 0); } else if (key.equals(CompositeResultData.SEPARATOR)) { String separatorString = content.getString(key); resultTable.newRowGroup().newRowSeparator(separatorString.charAt(0), true); } } addFooterInTable(resultTable, getGfJsonObject()); resultLines.addAll(resultTable.buildTableList()); } } catch (GfJsonException e) { resultLines .add("Error occurred while processing Command Result. Internal Error - Invalid Result."); LogWrapper.getInstance().info( "Error occurred while processing Command Result. Internal Error - Invalid Result.", e); } finally { isDataBuilt = true; } } private void buildSection(Table table, RowGroup parentRowGroup, GfJsonObject section, int depth) throws GfJsonException { Iterator<String> keys = section.keys(); RowGroup rowGroup = null; if (parentRowGroup != null) { rowGroup = parentRowGroup; } else { rowGroup = table.newRowGroup(); } addHeaderInRowGroup(rowGroup, section); while (keys.hasNext()) { String key = keys.next(); Object object = section.get(key); // System.out.println(key +" : " + object); if (key.startsWith(CompositeResultData.TABLE_DATA_ACCESSOR)) { GfJsonObject tableObject = section.getJSONObject(key); addHeaderInTable(table, tableObject); RowGroup rowGroupForTable = table.newRowGroup(); buildTable(rowGroupForTable, tableObject.getJSONObject(ResultData.RESULT_CONTENT)); addFooterInTable(table, tableObject); } else if (key.startsWith(CompositeResultData.SECTION_DATA_ACCESSOR)) { GfJsonObject subSection = section.getJSONObject(key); buildSection(table, rowGroup, subSection, depth + 1); } else if (key.equals(CompositeResultData.SEPARATOR)) { String separatorString = section.getString(key); rowGroup.newRowSeparator(separatorString.charAt(0), true); } else if (key.equals(ResultData.RESULT_HEADER) || key.equals(ResultData.RESULT_FOOTER)) { // skip header & footer continue; } else { Row newRow = rowGroup.newRow(); String prefix = ""; for (int i = 0; i < depth; i++) { prefix += " . "; } String[] value = getValuesSeparatedByLines(object); if (value.length == 1) { newRow.newLeftCol(prefix + key).newLeftCol(value[0]); } else { if (value.length != 0) { // possible when object == CliConstants.LINE_SEPARATOR newRow.newLeftCol(prefix + key).newLeftCol(value[0]); for (int i = 1; i < value.length; i++) { newRow = rowGroup.newRow(); newRow.setColumnSeparator(" "); newRow.newLeftCol("").newLeftCol(value[i]); } } else { newRow.newLeftCol(prefix + key).newLeftCol(""); } } // System.out.println(key+" : "+object); } } addFooterInRowGroup(rowGroup, section); } // public static void main(String[] args) { // String[] valuesSeparatedByLines = getValuesSeparatedByLines(CliConstants.LINE_SEPARATOR); // System.out.println(valuesSeparatedByLines +" -- "+valuesSeparatedByLines.length); // } private static String[] getValuesSeparatedByLines(Object object) { String[] values = null; String valueString = String.valueOf(object); values = valueString.split(GfshParser.LINE_SEPARATOR); return values; } /** * @param rowGroup * @param content * @throws GfJsonException */ private void buildTable(RowGroup rowGroup, GfJsonObject content) throws GfJsonException { GfJsonArray columnNames = content.names(); int numOfColumns = columnNames.size(); Row headerRow = rowGroup.newRow(); rowGroup.setColumnSeparator(" | "); rowGroup.newRowSeparator('-', false); // build Table Header first for (int i = 0; i < numOfColumns; i++) { Object object = columnNames.get(i); if (AbstractResultData.BYTE_DATA_ACCESSOR.equals(object)) { // skip file data if any //TODO - make response format better continue; } headerRow.newCenterCol((String) object); } // Build remaining rows by extracting data column-wise from JSON object Row[] dataRows = null; for (int i = 0; i < numOfColumns; i++) { Object object = columnNames.get(i); if (AbstractResultData.BYTE_DATA_ACCESSOR.equals(object)) { // skip file data if any //TODO - make response format better continue; } GfJsonArray accumulatedData = content.getJSONArray((String) object); dataRows = buildRows(rowGroup, dataRows, accumulatedData); } } /** * @param rowGroup * @param dataRows * @param accumulatedData * @return rows * @throws GfJsonException */ private Row[] buildRows(RowGroup rowGroup, Row[] dataRows, GfJsonArray accumulatedData) throws GfJsonException { int size = accumulatedData.size(); // Initialize rows' array as required if (dataRows == null) { dataRows = new Row[size]; for (int j = 0; j < dataRows.length; j++) { dataRows[j] = rowGroup.newRow(); } } // Add data column-wise for (int j = 0; j < size; j++) { dataRows[j].newLeftCol(accumulatedData.get(j)); } return dataRows; } public boolean hasIncomingFiles() { GfJsonArray fileDataArray = null; try { GfJsonObject content = getContent(); if (content != null) { fileDataArray = content.getJSONArray(CompositeResultData.BYTE_DATA_ACCESSOR); } } catch (GfJsonException e) { e.printStackTrace(); } return fileDataArray != null; } public int getNumTimesSaved() { return numTimesSaved; } public void saveIncomingFiles(String directory) throws IOException { // dump file data if any try { GfJsonObject content = getContent(); if (content != null) { GfJsonArray bytesArray = content.getJSONArray(CompositeResultData.BYTE_DATA_ACCESSOR); AbstractResultData.readFileDataAndDump(bytesArray, directory); } else { throw new RuntimeException("No associated files to save .. "); // TODO Abhishek - add i18n // string } numTimesSaved = numTimesSaved + 1; } catch (DataFormatException e) { throw new RuntimeException(e); } catch (GfJsonException e) { throw new RuntimeException(e); } } @Override public boolean hasNextLine() { if (!isDataBuilt) { buildData(); } return index < resultLines.size(); } /** * @throws ArrayIndexOutOfBoundsException if this method is called more number of times than the * data items it contains */ @Override public String nextLine() { if (!isDataBuilt) { buildData(); } return resultLines.get(index++); } public String toJson() { return gfJsonObject.toString(); } public String getType() { return resultData.getType(); } public String getHeader() { return getHeader(gfJsonObject); } public String getHeader(GfJsonObject gfJsonObject) { return gfJsonObject.getString(ResultData.RESULT_HEADER); } public GfJsonObject getContent() throws GfJsonException { return gfJsonObject.getJSONObject(ResultData.RESULT_CONTENT); } // public String getContentStr() { // return gfJsonObject.getString(ResultData.RESULT_CONTENT); // } public String getFooter() { return getFooter(gfJsonObject); } public String getFooter(GfJsonObject gfJsonObject) { return gfJsonObject.getString(ResultData.RESULT_FOOTER); } @Override public boolean equals(Object obj) { if (!(obj instanceof CommandResult)) { return false; } CommandResult other = (CommandResult) obj; return this.gfJsonObject.toString().equals(other.gfJsonObject.toString()); } public int hashCode() { return this.gfJsonObject.hashCode(); // any arbitrary constant will do } @Override public String toString() { return "CommandResult [gfJsonObject=" + gfJsonObject + ", status=" + status + ", index=" + index + ", isDataBuilt=" + isDataBuilt + ", resultData=" + resultData + ", resultLines=" + resultLines + ", failedToPersist=" + failedToPersist + "]"; } @Override public boolean failedToPersist() { return this.failedToPersist; } @Override public void setCommandPersisted(boolean commandPersisted) { this.failedToPersist = !commandPersisted; } }