View Javadoc
1   /*
2    * Copyright 2001-2008 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  package org.apache.juddi.validation;
18  
19  import java.util.Date;
20  import java.util.GregorianCalendar;
21  import java.util.HashSet;
22  import java.util.List;
23  
24  import javax.persistence.EntityManager;
25  import javax.xml.datatype.DatatypeConfigurationException;
26  import javax.xml.datatype.DatatypeFactory;
27  import org.apache.commons.configuration.ConfigurationException;
28  import org.apache.juddi.config.AppConfig;
29  import org.apache.juddi.config.Property;
30  
31  import org.apache.juddi.keygen.KeyGenerator;
32  import org.apache.juddi.keygen.KeyGeneratorFactory;
33  import org.apache.juddi.model.UddiEntityPublisher;
34  import org.apache.juddi.v3.error.ErrorMessage;
35  import org.apache.juddi.v3.error.FatalErrorException;
36  import org.apache.juddi.v3.error.InvalidKeyPassedException;
37  import org.apache.juddi.v3.error.InvalidTimeException;
38  import org.apache.juddi.v3.error.KeyUnavailableException;
39  import org.apache.juddi.v3.error.UserMismatchException;
40  import org.apache.juddi.v3.error.ValueNotAllowedException;
41  import org.uddi.sub_v3.CoveragePeriod;
42  import org.uddi.sub_v3.DeleteSubscription;
43  import org.uddi.sub_v3.GetSubscriptionResults;
44  import org.uddi.sub_v3.SubscriptionFilter;
45  import org.uddi.v3_service.DispositionReportFaultMessage;
46  
47  /**
48   * @author <a href="mailto:jfaath@apache.org">Jeff Faath</a>
49   */
50  public class ValidateSubscription extends ValidateUDDIApi {
51  
52      public ValidateSubscription(UddiEntityPublisher publisher) {
53          super(publisher);
54      }
55      private DatatypeFactory df = null;
56  
57      public void validateSubscriptions(EntityManager em, List<org.uddi.sub_v3.Subscription> entityList, UddiEntityPublisher publisher) throws DispositionReportFaultMessage {
58  
59          // No null or empty list
60          if (entityList == null || entityList.size() == 0) {
61              throw new ValueNotAllowedException(new ErrorMessage("errors.savesubscription.NoInput"));
62          }
63  
64          for (org.uddi.sub_v3.Subscription entity : entityList) {
65              validateSubscription(em, entity, publisher);
66          }
67      }
68  
69      private void validateSubscription(EntityManager em, org.uddi.sub_v3.Subscription subscription, UddiEntityPublisher publisher) throws DispositionReportFaultMessage {
70  
71          // A supplied subscription can't be null
72          if (subscription == null) {
73              throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.NullInput"));
74          }
75          if (df == null) {
76              try {
77                  df = DatatypeFactory.newInstance();
78              } catch (DatatypeConfigurationException ex) {
79                  throw new FatalErrorException(new ErrorMessage("errors.DatatypeFactor"));
80              }
81          }
82          boolean entityExists = false;
83          String entityKey = subscription.getSubscriptionKey();
84          //no key specified, make a new one
85          if (entityKey == null || entityKey.length() == 0) {
86              KeyGenerator keyGen = KeyGeneratorFactory.getKeyGenerator();
87              entityKey = keyGen.generate(publisher);
88              subscription.setSubscriptionKey(entityKey);
89          } else {
90              //key specified, validate it
91              // Per section 4.4: keys must be case-folded
92              entityKey = entityKey.toLowerCase();
93              subscription.setSubscriptionKey(entityKey);
94              ValidatePublish.validateKeyLength(entityKey);
95              Object obj = em.find(org.apache.juddi.model.Subscription.class, entityKey);
96              if (obj != null) {
97                  entityExists = true;
98                  //revising a new item
99                  // Make sure publisher owns this entity.
100                 if (!publisher.getAuthorizedName().equals(((org.apache.juddi.model.Subscription) obj).getAuthorizedName())) {
101                     throw new UserMismatchException(new ErrorMessage("errors.usermismatch.InvalidOwner", entityKey));
102                 }
103             } else {
104                 //new item
105                 // Inside this block, we have a key proposed by the publisher on a new entity
106 
107                 // Validate key and then check to see that the proposed key is valid for this publisher
108                 ValidateUDDIKey.validateUDDIv3Key(entityKey);
109                 
110                 if (!publisher.isValidPublisherKey(em, entityKey)) {
111                     throw new KeyUnavailableException(new ErrorMessage("errors.keyunavailable.BadPartition", entityKey));
112                 }
113 
114             }
115 
116         }
117 
118         if (!entityExists) {
119             // Check to make sure key isn't used by another entity.
120             if (!isUniqueKey(em, entityKey)) {
121                 throw new KeyUnavailableException(new ErrorMessage("errors.keyunavailable.KeyExists", entityKey));
122             }
123         }
124         //AO, if it's already expired, why even allow it?
125         if (subscription.getExpiresAfter() != null) {
126             long expiresat = subscription.getExpiresAfter().toGregorianCalendar().getTimeInMillis();
127             if (System.currentTimeMillis() > expiresat) {
128                 throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.expired"));
129             }
130         }
131         if (subscription.getMaxEntities() != null) {
132             if (subscription.getMaxEntities().intValue() <= 0) {
133                 throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.maxrecordstoosmall"));
134             }
135         }
136         //maxEntities:  This optional integer specifies the maximum number of entities in a notification returned to a subscription listener. 
137         //If not specified, the number of entities sent is not limited, unless by node policy.
138         try {
139             if (subscription.getMaxEntities() == null
140                     || subscription.getMaxEntities().intValue() > AppConfig.getConfiguration().getInt(Property.JUDDI_SUBSCRIPTION_MAXENTITIES, 1000)) {
141                 subscription.setMaxEntities(AppConfig.getConfiguration().getInt(Property.JUDDI_SUBSCRIPTION_MAXENTITIES, 1000));
142             }
143         } catch (ConfigurationException x) {
144             subscription.setMaxEntities(1000);
145         }
146         /*
147          *  notificationInterval:  This optional argument is only required when asynchronous notifications are used. 
148          * It is of type xsd:duration and specifies how often change notifications are to be provided to a subscriber. 
149          * If the notificationInterval specified is not acceptable due to node policy, then the node adjusts the value to
150          * match the next longer time period that is supported.  The adjusted value is provided with the returns from this
151          * API.  Also see Section 5.5.1.1 Specifying Durations.
152          */
153         if (subscription.getNotificationInterval() == null && subscription.getBindingKey() != null) {
154             throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.notificationintervalnotdefined"));
155         }
156         
157         //validate that the binding key exists
158         validateSubscriptionBindingkeyExists(em,subscription.getBindingKey());
159         
160         validateSubscriptionFilter(subscription.getSubscriptionFilter(), entityExists);
161     }
162 
163     /**
164      * this handles just the filter items only
165      *
166      * @param subscriptionFilter
167      * @param entityExists or more accurately, is this a new item or not?
168      * @throws DispositionReportFaultMessage
169      */
170     private void validateSubscriptionFilter(SubscriptionFilter subscriptionFilter, boolean entityExists) throws DispositionReportFaultMessage {
171         if (!entityExists && subscriptionFilter == null) {
172             throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.NoFilterOnNewSubscription"));
173         }
174 
175         if (subscriptionFilter != null) {
176             int filterCount = 0;
177             ValidateInquiry validateInquiry = new ValidateInquiry(publisher);
178             if (subscriptionFilter.getFindBinding() != null) {
179                 filterCount++;
180                 validateInquiry.validateFindBinding(subscriptionFilter.getFindBinding());
181             }
182             if (subscriptionFilter.getFindBusiness() != null) {
183                 filterCount++;
184                 validateInquiry.validateFindBusiness(subscriptionFilter.getFindBusiness());
185             }
186             if (subscriptionFilter.getFindService() != null) {
187                 filterCount++;
188                 validateInquiry.validateFindService(subscriptionFilter.getFindService());
189             }
190             if (subscriptionFilter.getFindTModel() != null) {
191                 filterCount++;
192                 validateInquiry.validateFindTModel(subscriptionFilter.getFindTModel(), false);
193             }
194             if (subscriptionFilter.getFindRelatedBusinesses() != null) {
195                 filterCount++;
196                 validateInquiry.validateFindRelatedBusinesses(subscriptionFilter.getFindRelatedBusinesses(), false);
197             }
198             if (subscriptionFilter.getGetBindingDetail() != null) {
199                 filterCount++;
200                 validateInquiry.validateGetBindingDetail(subscriptionFilter.getGetBindingDetail());
201             }
202             if (subscriptionFilter.getGetBusinessDetail() != null) {
203                 filterCount++;
204                 validateInquiry.validateGetBusinessDetail(subscriptionFilter.getGetBusinessDetail());
205             }
206             if (subscriptionFilter.getGetServiceDetail() != null) {
207                 filterCount++;
208                 validateInquiry.validateGetServiceDetail(subscriptionFilter.getGetServiceDetail());
209             }
210             if (subscriptionFilter.getGetTModelDetail() != null) {
211                 filterCount++;
212                 validateInquiry.validateGetTModelDetail(subscriptionFilter.getGetTModelDetail());
213             }
214             if (subscriptionFilter.getGetAssertionStatusReport() != null) {
215                 filterCount++;
216             }
217 
218             if (filterCount == 0) {
219                 throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.BlankFilter"));
220             }
221             //the spec defines subscription filters as a switch, exactly one is required
222             if (filterCount > 1) {
223                 throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.TooManyFilters", String.valueOf(filterCount)));
224             }
225 
226         }
227 
228     }
229 
230     public void validateDeleteSubscription(EntityManager em, DeleteSubscription body) throws DispositionReportFaultMessage {
231         // No null input
232         if (body == null) {
233             throw new FatalErrorException(new ErrorMessage("errors.NullInput"));
234         }
235 
236         // No null or empty list
237         List<String> entityKeyList = body.getSubscriptionKey();
238         if (entityKeyList == null || entityKeyList.size() == 0) {
239             throw new InvalidKeyPassedException(new ErrorMessage("errors.invalidkey.NoKeys"));
240         }
241 
242         HashSet<String> dupCheck = new HashSet<String>();
243         int i = 0;
244         for (String entityKey : entityKeyList) {
245 
246             // Per section 4.4: keys must be case-folded
247             entityKey = entityKey.toLowerCase();
248             entityKeyList.set(i, entityKey);
249 
250             boolean inserted = dupCheck.add(entityKey);
251             if (!inserted) {
252                 throw new InvalidKeyPassedException(new ErrorMessage("errors.invalidkey.DuplicateKey", entityKey));
253             }
254 
255             Object obj = em.find(org.apache.juddi.model.Subscription.class, entityKey);
256             if (obj == null) {
257                 throw new InvalidKeyPassedException(new ErrorMessage("errors.invalidkey.SubscriptionNotFound", entityKey));
258             }
259 
260             // Make sure publisher owns this entity.
261             if (!publisher.getAuthorizedName().equals(((org.apache.juddi.model.Subscription) obj).getAuthorizedName())) {
262                 throw new UserMismatchException(new ErrorMessage("errors.usermismatch.InvalidOwner", entityKey));
263             }
264 
265             i++;
266         }
267     }
268 
269     public void validateGetSubscriptionResults(EntityManager em, GetSubscriptionResults body) throws DispositionReportFaultMessage {
270         // No null input
271         if (body == null) {
272             throw new FatalErrorException(new ErrorMessage("errors.NullInput"));
273         }
274 
275         String subscriptionKey = body.getSubscriptionKey();
276         if (subscriptionKey == null || subscriptionKey.length() == 0) {
277             throw new InvalidKeyPassedException(new ErrorMessage("errors.invalidkey.NullKey", subscriptionKey));
278         }
279 
280         // Per section 4.4: keys must be case-folded
281         subscriptionKey = subscriptionKey.toLowerCase();
282         body.setSubscriptionKey(subscriptionKey);
283 
284         Object obj = em.find(org.apache.juddi.model.Subscription.class, subscriptionKey);
285         if (obj == null) {
286             throw new InvalidKeyPassedException(new ErrorMessage("errors.invalidkey.SubscriptionNotFound", subscriptionKey));
287         }
288 
289         Date expiresAfter = ((org.apache.juddi.model.Subscription) obj).getExpiresAfter();
290         Date now = new Date();
291         if (expiresAfter.getTime() < now.getTime()) {
292             throw new InvalidKeyPassedException(new ErrorMessage("errors.getsubscriptionresult.SubscriptionExpired", subscriptionKey));
293         }
294 
295         CoveragePeriod coveragePeriod = body.getCoveragePeriod();
296         if (coveragePeriod == null) {
297             throw new InvalidTimeException(new ErrorMessage("errors.getsubscriptionresult.NullCoveragePeriod"));
298         }
299 
300         if (coveragePeriod.getStartPoint() == null || coveragePeriod.getEndPoint() == null) {
301             throw new InvalidTimeException(new ErrorMessage("errors.getsubscriptionresult.InvalidDateInCoveragePeriod"));
302         }
303 
304         GregorianCalendar startPoint = coveragePeriod.getStartPoint().toGregorianCalendar();
305         GregorianCalendar endPoint = coveragePeriod.getEndPoint().toGregorianCalendar();
306         if (startPoint.getTimeInMillis() > endPoint.getTimeInMillis()) {
307             throw new InvalidTimeException(new ErrorMessage("errors.getsubscriptionresult.StartPointAfterEndPoint", startPoint.toString()));
308         }
309     }
310 
311     private void validateSubscriptionBindingkeyExists(EntityManager em, String bindingKey) throws ValueNotAllowedException {
312         if (bindingKey==null || bindingKey.length()==0) {
313             return;
314         }
315         Object obj = em.find(org.apache.juddi.model.BindingTemplate.class, bindingKey);
316         if (obj==null) {
317             throw new ValueNotAllowedException(new ErrorMessage("errors.subscription.BindingDoesntExist",bindingKey));
318         }
319     }
320 }