/* * @(#)Status.java * * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ package com.sun.xacml.ctx; import com.sun.xacml.Constants; import com.sun.xacml.Indenter; import com.sun.xacml.ParsingException; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Represents the status data that is included in a ResultType. By default, * the status is OK. * * @since 1.0 * @author Seth Proctor */ public class Status { /** * Standard identifier for the OK status */ public static final String STATUS_OK = "urn:oasis:names:tc:xacml:1.0:status:ok"; /** * Standard identifier for the MissingAttribute status */ public static final String STATUS_MISSING_ATTRIBUTE = "urn:oasis:names:tc:xacml:1.0:status:missing-attribute"; /** * Standard identifier for the SyntaxError status */ public static final String STATUS_SYNTAX_ERROR = "urn:oasis:names:tc:xacml:1.0:status:syntax-error"; /** * Standard identifier for the ProcessingError status */ public static final String STATUS_PROCESSING_ERROR = "urn:oasis:names:tc:xacml:1.0:status:processing-error"; // the status code private List<String> code; // the message private String message; // the detail private StatusDetail detail; // a single OK object we'll use most of the time private static Status okStatus; // initialize the OK Status object static { List<String> code = new ArrayList<String>(); code.add(STATUS_OK); okStatus = new Status(code); } /** * Constructor that takes only the status code. * * @param code a <code>List</code> of <code>String</code> codes, typically * just one code, but this may contain any number of minor * codes after the first item in the list, which is the major * code */ public Status(List<String> code) { this(code, null, null); } /** * Constructor that takes both the status code and a message to include * with the status. * * @param code a <code>List</code> of <code>String</code> codes, typically * just one code, but this may contain any number of minor * codes after the first item in the list, which is the major * code * @param message a message to include with the code */ public Status(List<String> code, String message) { this(code, message, null); } /** * Constructor that takes the status code, an optional message, and some * detail to include with the status. Note that the specification * explicitly says that a status code of OK, SyntaxError or * ProcessingError may not appear with status detail, so an exception is * thrown if one of these status codes is used and detail is included. * * @param code a <code>List</code> of <code>String</code> codes, typically * just one code, but this may contain any number of minor * codes after the first item in the list, which is the major * code * @param message a message to include with the code, or null if there * should be no message * @param detail the status detail to include, or null if there is no * detail * * @throws IllegalArgumentException if detail is included for a status * code that doesn't allow detail */ public Status(List<String> code, String message, StatusDetail detail) throws IllegalArgumentException { // if the code is ok, syntax error or processing error, there // must not be any detail included if (detail != null) { String c = (String)(code.iterator().next()); if (c.equals(STATUS_OK) || c.equals(STATUS_SYNTAX_ERROR) || c.equals(STATUS_PROCESSING_ERROR)) { throw new IllegalArgumentException("status detail cannot be " + "included with " + c); } } this.code = Collections.unmodifiableList(new ArrayList<String>(code)); this.message = message; this.detail = detail; } /** * Returns the status code. * * @return the status code */ public List<String> getCode() { return this.code; } /** * Returns the status message or null if there is none. * * @return the status message or null */ public String getMessage() { return this.message; } /** * Returns the status detail or null if there is none. * * @return a <code>StatusDetail</code> or null */ public StatusDetail getDetail() { return this.detail; } /** * Gets a <code>Status</code> instance that has the OK status and no * other information. This is the default status data for all responses * except Indeterminate ones. * * @return an instance with <code>STATUS_OK</code> */ public static Status getOkInstance() { return okStatus; } /** * Creates a new instance of <code>Status</code> based on the given * DOM root node. A <code>ParsingException</code> is thrown if the DOM * root doesn't represent a valid StatusType. * * @param root the DOM root of a StatusType * * @return a new <code>Status</code> * * @throws ParsingException if the node is invalid */ public static Status getInstance(Node root) throws ParsingException { if (root.getNodeType() != Node.ELEMENT_NODE || !root.getLocalName().equals("Status")) { throw new ParsingException("Can't create a Status from a " + root.getLocalName() + " element"); } List<String> code = null; String message = null; StatusDetail detail = null; NodeList nodes = root.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String name = node.getLocalName(); if (name.equals("StatusCode")) { code = parseStatusCode(node); } else if (name.equals("StatusMessage")) { message = node.getFirstChild().getNodeValue(); } else if (name.equals("StatusDetail")) { detail = StatusDetail.getInstance(node); } } } return new Status(code, message, detail); } /** * Private helper that parses the status code */ private static List<String> parseStatusCode(Node root) { // get the top-level code String val = root.getAttributes().getNamedItem("Value").getNodeValue(); List<String> code = new ArrayList<String>(); code.add(val); // now get the list of all sub-codes, and work through them NodeList list = ((Element)root).getElementsByTagName("StatusCode"); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); code.add(node.getAttributes().getNamedItem("Value"). getNodeValue()); } return code; } /** * Encodes this status data into its XML representation and writes * this encoding to the given <code>OutputStream</code> with no * indentation. * * @param output a stream into which the XML-encoded data is written @param charsetName the character set to use in encoding of strings. * This may be null in which case the platform * default character set will be used. * * @throws UnsupportedEncodingException */ public void encode(OutputStream output, String charsetName) throws UnsupportedEncodingException { encode(output, charsetName, new Indenter(0)); } /** * Encodes this status data into its XML representation and writes * this encoding to the given <code>OutputStream</code> with * indentation. * * @param output a stream into which the XML-encoded data is written @param charsetName the character set to use in encoding of strings. * This may be null in which case the platform * default character set will be used. * @param indenter an object that creates indentation strings * * @throws UnsupportedEncodingException */ public void encode(OutputStream output, String charsetName, Indenter indenter) throws UnsupportedEncodingException { PrintStream out; if(charsetName == null) { out = new PrintStream(output); } else { out = new PrintStream(output, false, charsetName); } String indent = indenter.makeString(); out.println(indent + "<Status>"); indenter.in(); encodeStatusCode(out, indenter, this.code.iterator()); if (this.message != null) { out.println(indenter.makeString() + "<StatusMessage>" + this.message + "</StatusMessage>"); } if (this.detail != null) { out.println(this.detail.getEncoded()); } indenter.out(); out.println(indent + "</Status>"); } /** * Encodes the object in XML */ private void encodeStatusCode(PrintStream out, Indenter indenter, Iterator<String> iterator) { String in = indenter.makeString(); String code = iterator.next(); if (iterator.hasNext()) { indenter.in(); out.println(in + "<StatusCode Value=\"" + code + "\">"); encodeStatusCode(out, indenter, iterator); out.println(in + "</StatusCode>"); indenter.out(); } else { out.println(in + "<StatusCode Value=\"" + code + "\"/>"); } } /** * @param indenter * @return the string representation of this status message * encoded in XML */ public String toString(Indenter indenter) { String indent = indenter.makeString(); String result = indent + "<Status>" + Constants.nl; indenter.in(); result += statusCode2String(indenter, this.code.iterator()); if (this.message != null) { result += indenter.makeString() + "<StatusMessage>" + this.message + "</StatusMessage>" + Constants.nl; } if (this.detail != null) { result += indenter.makeString() + this.detail.getEncoded(); } indenter.out(); result += indent + "</Status>" + Constants.nl; return result; } private String statusCode2String(Indenter indenter, Iterator<String> it) { String in = indenter.makeString(); String code = it.next(); String result; if (it.hasNext()) { indenter.in(); result = in + "<StatusCode Value=\"" + code + "\">" + Constants.nl; result += statusCode2String(indenter, it); result += in + "</StatusCode>" + Constants.nl; indenter.out(); } else { result = in + "<StatusCode Value=\"" + code + "\"/>" + Constants.nl; } return result; } public static Status createStatus(String code, String message) { List<String> codes = new ArrayList<String>(); codes.add(code); return new Status(codes, message); } }