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 &lt;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 &lt;a href= &quot;http://cyber.law.harvard.edu/rss/rss.html#
034 * ltcloudgtSubelementOfLtchannelgt&quot; &gt;here&lt;/a&gt;
035 * </p>
036 * 
037 * <p>
038 * &lt;cloud&gt; sub-element of &lt;channel&gt;
039 * </p>
040 * 
041 * <p>
042 * &lt;cloud&gt; is an optional sub-element of &lt;channel&gt;.
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 * &lt;cloud domain=&quot;rpc.sys.com&quot; port=&quot;80&quot;
058 * path=&quot;/RPC2&quot; registerProcedure=&quot;myCloud.rssPleaseNotify&quot;
059 * protocol=&quot;xml-rpc&quot; /&gt;
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=&quot
071 * ;http://cyber.law.harvard.edu/rss/soapMeetsRss.html#rsscloudInterface&quot;
072 * &gt;here&lt;/a&gt;.
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 &lt;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}