All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.admingui.common.util.RestResponse Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2018-2021] Payara Foundation and/or affiliates

package org.glassfish.admingui.common.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.ws.rs.core.Response;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 

This class abstracts the response from the admin console code so that * we can use JSON / REST interchangeably.

* * @author jasonlee * @author Ken Paulsen ([email protected]) */ public abstract class RestResponse { public abstract int getResponseCode(); public abstract String getResponseBody(); public static RestResponse getRestResponse(Response response) { return new JerseyRestResponse(response); } public boolean isSuccess() { int status = getResponseCode(); return (status >= 200) && (status <= 299); } /** *

This method abstracts the physical response to return a consistent * data structure. For many responses, this data structure may look * like:

*

*

* * Map<String, Object> * { * "responseCode" : Integer // HTTP Response code, ie. 200 * "output" : String // The Raw Response Body * "description" : String // Command Description * // 0 or more messages returned from the command * "messages" : List<Map<String, Object>> * [ * { * "message" : String // Raw Message String * "..." : String // Additional custom attributes * // List of properties for this message * "properties" : List<Map<String, Object>> * [ * { * "name" : String // The Property Name * "value" : String // The Property Value * "properties" : List // Child Properties * }, ... * ] * }, ... * ] * } * *

*/ public abstract Map getResponse(); public abstract void close(); } class JerseyRestResponse extends RestResponse { private static final Logger LOG = Logger.getLogger(JerseyRestResponse.class.getName()); protected Response response; private String body = null; public JerseyRestResponse(Response response) { this.response = response; } @Override public String getResponseBody() { if (body == null) { body = response.readEntity(String.class); } return body; } @Override public int getResponseCode() { return response.getStatus(); } /** *

This method abstracts the physical response to return a consistent * data structure.

*/ @Override public Map getResponse() { LOG.finest("getResponse()"); // Prepare the result object Map result = new HashMap<>(5); // Add the Response Code result.put("responseCode", getResponseCode()); result.put("responseBody", getResponseBody()); String contentType = response.getHeaderString("Content-type"); if (contentType != null) { String responseBody = getResponseBody(); contentType = contentType.toLowerCase(GuiUtil.guiLocale); if (contentType.startsWith("application/xml")) { InputStream input = null; try { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); inputFactory.setProperty(XMLInputFactory.IS_VALIDATING, false); input = new ByteArrayInputStream(responseBody.trim().getBytes("UTF-8")); XMLStreamReader parser = inputFactory.createXMLStreamReader(input); while (parser.hasNext()) { int event = parser.next(); switch (event) { case XMLStreamConstants.START_ELEMENT: { if ("map".equals(parser.getLocalName())) { result.put("data", processXmlMap(parser)); } break; } default: break; } } } catch (Exception ex) { Logger.getLogger(RestResponse.class.getName()).log(Level.SEVERE, null, ex); throw new RuntimeException(ex); } finally { try { if (input != null){ input.close(); } } catch (IOException ex) { Logger.getLogger(RestResponse.class.getName()).log(Level.SEVERE, null, ex); } } // // If XML... // Document document = MiscUtil.getDocument(getResponseBody()); // Element root = document.getDocumentElement(); // if ("action-report".equalsIgnoreCase(root.getNodeName())) { // // Default XML document type... // // Add the Command Description // result.put("description", root.getAttribute("description")); // result.put("exit-code", root.getAttribute("exit-code")); // // // Add the messages // List> messages = new ArrayList>(2); // result.put("messages", messages); // // // Iterate over each node looking for message-part // NodeList nl = root.getChildNodes(); // int len = nl.getLength(); // Node child; // for (int idx = 0; idx < len; idx++) { // child = nl.item(idx); // if ((child.getNodeType() == Node.ELEMENT_NODE) && (child.getNodeName().equals("message-part"))) { // messages.add(processMessagePart(child)); // } // } // } else { // // Generate a generic Java structure from the XML // result.put("data", getJavaFromXML(root)); // } } else if (contentType.startsWith("application/json")) { // Decode JSON JsonReader reader = Json.createReader(new StringReader(responseBody)); JsonObject object = reader.readObject(); result.put("data", JsonUtil.jsonObjectToMap(object)); } else { LOG.severe("Unsupported Response Format: '" + contentType + "'!"); } } // Return the populated result data structure return result; } /** *

This method will create a Map. It will add all the * attributes of the given root Element to the Map. It will then walk * any child Elements and add the children as a * List<Map<String, Object>> for each unique * element name.

*/ private Map getJavaFromXML(Element element) { // Create a new Map to store the properties and children. Map result = new HashMap<>(10); // Add all the attributes... NamedNodeMap attributes = element.getAttributes(); int attLen = attributes.getLength(); for (int attIdx = 0; attIdx < attLen; attIdx++) { Node attribute = attributes.item(attIdx); result.put(attribute.getNodeName(), attribute.getNodeValue()); } // Now add any child Elements String childName; Node child; List> childList; NodeList nl = element.getChildNodes(); int len = nl.getLength(); for (int idx = 0; idx < len; idx++) { child = nl.item(idx); if ((child.getNodeType() == Node.ELEMENT_NODE)) { // We found a child Element... childName = child.getNodeName(); if (result.containsKey(childName)) { // Already created, add to it childList = (List>) result.get(childName); } else { // Not created yet, create it childList = new ArrayList<>(5); result.put(childName, childList); } // Add the child to the List childList.add(getJavaFromXML((Element) child)); } } // Return the fully populated Map return result; } /** *

This method returns a fully populated Map for the * given "message-part" Node.

*/ private Map processMessagePart(Node messageNode) { // Create a Map to hold all the Message info... Map message = new HashMap<>(5); // Pull off all the attributes from the message... NamedNodeMap attributes = messageNode.getAttributes(); int attLen = attributes.getLength(); for (int attIdx = 0; attIdx < attLen; attIdx++) { // "message" should be one of them... add them all Node attribute = attributes.item(attIdx); message.put(attribute.getNodeName(), attribute.getNodeValue()); } // Now see if there are any child message-parts or child properties NodeList nl = messageNode.getChildNodes(); int len = nl.getLength(); Node child; boolean hasChildMessages = false; boolean hasProperty = false; List> properties = null; List> messages = null; for (int idx = 0; idx < len; idx++) { child = nl.item(idx); if ((child.getNodeType() == Node.ELEMENT_NODE)) { if (child.getNodeName().equals("message-part")) { // Recursively add this new message-part child if (!hasChildMessages) { // Create a List to hold the messages. messages = new ArrayList<>(2); message.put("messages", messages); hasChildMessages = true; } // Add the message messages.add(processMessagePart(child)); } else if (child.getNodeName().equals("property")) { // Add this new property if (!hasProperty) { // Create a List to hold the properties. properties = new ArrayList<>(10); message.put("properties", properties); hasProperty = true; } // Add the property properties.add(processProperty(child)); } } } // Return the populated message return message; } /** *

This method returns a fully populated Map for the * given "property" Node.

*/ private Map processProperty(Node propertyNode) { // Create a Map to hold all the Message info... Map property = new HashMap<>(5); // Pull off all the attributes from the property... NamedNodeMap attributes = propertyNode.getAttributes(); int attLen = attributes.getLength(); for (int attIdx = 0; attIdx < attLen; attIdx++) { // "name" and "value" should be the only 2, but add them all... Node attribute = attributes.item(attIdx); property.put(attribute.getNodeName(), attribute.getNodeValue()); } // Now see if there are any child properties NodeList nl = propertyNode.getChildNodes(); int len = nl.getLength(); Node child; boolean hasProperty = false; List> properties = null; for (int idx = 0; idx < len; idx++) { child = nl.item(idx); if ((child.getNodeType() == Node.ELEMENT_NODE)) { if (child.getNodeName().equals("property")) { // Add this new property if (!hasProperty) { // Create a List to hold the properties. properties = new ArrayList<>(10); property.put("properties", properties); hasProperty = true; } // Add the property properties.add(processProperty(child)); } } } // Return the populated property data structure return property; } private static Map processXmlMap(XMLStreamReader parser) throws XMLStreamException { boolean endOfMap = false; Map entry = new HashMap<>(); String key = null; String element = null; while (!endOfMap) { int event = parser.next(); switch (event) { case XMLStreamConstants.START_ELEMENT: { if ("entry".equals(parser.getLocalName())) { key = parser.getAttributeValue(null, "key"); String value = parser.getAttributeValue(null, "value"); if (value != null) { entry.put(key, value); key = null; } } else if ("map".equals(parser.getLocalName())) { Map value = processXmlMap(parser); entry.put(key, value); } else if ("list".equals(parser.getLocalName())) { List value = processXmlList(parser); entry.put(key, value); } else { element = parser.getLocalName(); } break; } case XMLStreamConstants.END_ELEMENT: { if ("map".equals(parser.getLocalName())) { endOfMap = true; } element = null; break; } default: { String text=parser.getText(); if (element != null) { if ("number".equals(element)) { if (text.contains(".")) { entry.put(key, Double.parseDouble(text)); } else { entry.put(key, Long.parseLong(text)); } } else if ("string".equals(element)) { entry.put(key, text); } element = null; } } } } return entry; } private static List processXmlList(XMLStreamReader parser) throws XMLStreamException { List list = new ArrayList<>(); boolean endOfList = false; String element = null; while (!endOfList) { int event = parser.next(); switch (event) { case XMLStreamConstants.START_ELEMENT: { if ("map".equals(parser.getLocalName())) { list.add(processXmlMap(parser)); } else { element = parser.getLocalName(); } break; } case XMLStreamConstants.END_ELEMENT: { if ("list".equals(parser.getLocalName())) { endOfList = true; } element = null; break; } default: { String text = parser.getText(); if (element != null) { if ("number".equals(element)) { if (text.contains(".")) { list.add(Double.parseDouble(text)); } else { list.add(Long.parseLong(text)); } } else if ("string".equals(element)) { list.add(text); } element = null; } } } } return list; } @Override public void close() { response.close(); } }