001/** 002 * Copyright 2011 Bill Brown 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.colorfulsoftware.rss; 017 018import java.io.Serializable; 019import java.util.LinkedList; 020import java.util.List; 021 022/** 023 * <p> 024 * The <cloud> element. 025 * </p> 026 * <p> 027 * From the <a href="http://cyber.law.harvard.edu/rss/rss.html">RSS 2.0 028 * specification</a>... 029 * </p> 030 * <p> 031 * Allows processes to register with a cloud to be notified of updates to the 032 * channel, implementing a lightweight publish-subscribe protocol for RSS feeds. 033 * More info <a href= "http://cyber.law.harvard.edu/rss/rss.html# 034 * ltcloudgtSubelementOfLtchannelgt" >here</a> 035 * </p> 036 * 037 * <p> 038 * <cloud> sub-element of <channel> 039 * </p> 040 * 041 * <p> 042 * <cloud> is an optional sub-element of <channel>. 043 * </p> 044 * 045 * <p> 046 * It specifies a web service that supports the rssCloud interface which can be 047 * implemented in HTTP-POST, XML-RPC or SOAP 1.1. 048 * </p> 049 * 050 * <p> 051 * Its purpose is to allow processes to register with a cloud to be notified of 052 * updates to the channel, implementing a lightweight publish-subscribe protocol 053 * for RSS feeds. 054 * </p> 055 * 056 * <p> 057 * <cloud domain="rpc.sys.com" port="80" 058 * path="/RPC2" registerProcedure="myCloud.rssPleaseNotify" 059 * protocol="xml-rpc" /> 060 * </p> 061 * 062 * <p> 063 * In this example, to request notification on the channel it appears in, you 064 * would send an XML-RPC message to rpc.sys.com on port 80, with a path of 065 * /RPC2. The procedure to call is myCloud.rssPleaseNotify. 066 * </p> 067 * 068 * <p> 069 * A full explanation of this element and the rssCloud interface is <a 070 * href=" 071 * ;http://cyber.law.harvard.edu/rss/soapMeetsRss.html#rsscloudInterface" 072 * >here</a>. 073 * </p> 074 * 075 * 076 * @author Bill Brown 077 * 078 */ 079public class Cloud implements Serializable { 080 081 /** 082 * 083 */ 084 private static final long serialVersionUID = 4431999134564899474L; 085 086 private final List<Attribute> attributes; 087 private final Attribute domain; 088 private final Attribute port; 089 private final Attribute path; 090 private final Attribute registerProcedure; 091 private final Attribute protocol; 092 093 Cloud(List<Attribute> attributes) throws RSSpectException { 094 if (attributes == null) { 095 throw new RSSpectException( 096 "The cloud element requires attributes: See \"http://cyber.law.harvard.edu/rss/soapMeetsRss.html#rsscloudInterface\"."); 097 } else { 098 this.attributes = new LinkedList<Attribute>(); 099 for (Attribute attr : attributes) { 100 // check for unsupported attribute. 101 this.attributes.add(new Attribute(attr.getName(), attr 102 .getValue())); 103 } 104 } 105 106 if ((this.domain = getAttribute("domain")) == null) { 107 throw new RSSpectException( 108 "cloud elements MUST have a domain attribute."); 109 } 110 111 if ((this.port = getAttribute("port")) == null) { 112 throw new RSSpectException( 113 "cloud elements MUST have a port attribute."); 114 } 115 116 if ((this.path = getAttribute("path")) == null) { 117 throw new RSSpectException( 118 "cloud elements MUST have a path attribute."); 119 } 120 121 if ((this.registerProcedure = getAttribute("registerProcedure")) == null) { 122 throw new RSSpectException( 123 "cloud elements MUST have a registerProcedure attribute."); 124 } 125 126 if ((this.protocol = getAttribute("protocol")) == null) { 127 throw new RSSpectException( 128 "cloud elements MUST have a protocol attribute."); 129 } 130 if (!getAttribute("protocol").getValue().equals("xml-rpc") 131 && !getAttribute("protocol").getValue().equals("soap")) { 132 throw new RSSpectException( 133 "the cloud's protocol attribute must be 'xml-rpc' or 'soap', case-sensitive."); 134 } 135 } 136 137 Cloud(Cloud cloud) { 138 this.attributes = cloud.getAttributes(); 139 this.domain = cloud.getDomain(); 140 this.port = cloud.getPort(); 141 this.path = cloud.getPath(); 142 this.registerProcedure = cloud.getRegisterProcedure(); 143 this.protocol = cloud.getProtocol(); 144 } 145 146 /** 147 * 148 * @return the cloud attribute list. 149 */ 150 public List<Attribute> getAttributes() { 151 152 List<Attribute> attrsCopy = new LinkedList<Attribute>(); 153 for (Attribute attr : this.attributes) { 154 attrsCopy.add(new Attribute(attr)); 155 } 156 return attrsCopy; 157 } 158 159 /** 160 * @return the domain attribute. 161 */ 162 public Attribute getDomain() { 163 return new Attribute(domain); 164 } 165 166 /** 167 * @return the port attribute 168 */ 169 public Attribute getPort() { 170 return new Attribute(port); 171 } 172 173 /** 174 * @return the path attribute 175 */ 176 public Attribute getPath() { 177 return new Attribute(path); 178 } 179 180 /** 181 * @return the registerProcedure attribute. 182 */ 183 public Attribute getRegisterProcedure() { 184 return new Attribute(registerProcedure); 185 } 186 187 /** 188 * @return the protocol attribute. 189 */ 190 public Attribute getProtocol() { 191 return new Attribute(protocol); 192 } 193 194 /** 195 * @param attrName 196 * the name of the attribute to get. 197 * @return the Attribute object if attrName matches or null if not found. 198 */ 199 public Attribute getAttribute(String attrName) { 200 for (Attribute attribute : this.attributes) { 201 if (attribute.getName().equals(attrName)) { 202 return new Attribute(attribute); 203 } 204 } 205 return null; 206 } 207 208 /** 209 * Shows the contents of the <cloud> element. 210 */ 211 @Override 212 public String toString() { 213 StringBuilder sb = new StringBuilder("<cloud"); 214 for (Attribute attribute : attributes) { 215 sb.append(attribute); 216 } 217 sb.append(" />"); 218 return sb.toString(); 219 } 220 221 @Override 222 public boolean equals(Object obj) { 223 if (obj == this) { 224 return true; 225 } 226 if (!(obj instanceof Cloud)) { 227 return false; 228 } 229 return this.toString().equals(obj.toString()); 230 } 231 232 @Override public int hashCode() { 233 return toString().hashCode(); 234 } 235}