001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2007, Red Hat Middleware, LLC, and individual *
004: * contributors as indicated by the @authors tag. See the *
005: * copyright.txt in the distribution for a full listing of *
006: * individual contributors. *
007: * *
008: * This is free software; you can redistribute it and/or modify it *
009: * under the terms of the GNU Lesser General Public License as *
010: * published by the Free Software Foundation; either version 2.1 of *
011: * the License, or (at your option) any later version. *
012: * *
013: * This software is distributed in the hope that it will be useful, *
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
016: * Lesser General Public License for more details. *
017: * *
018: * You should have received a copy of the GNU Lesser General Public *
019: * License along with this software; if not, write to the Free *
020: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
021: * 02110-1301 USA, or see the FSF site: http://www.fsf.org. *
022: ******************************************************************************/package org.jboss.portal.wsrp.consumer;
023:
024: import org.jboss.logging.Logger;
025: import org.jboss.portal.common.util.ParameterValidation;
026: import org.jboss.portal.portlet.PortletInvokerException;
027: import org.jboss.portal.wsrp.WSRPConstants;
028: import org.jboss.portal.wsrp.WSRPTypeFactory;
029: import org.jboss.portal.wsrp.WSRPUtils;
030: import org.jboss.portal.wsrp.core.ModelDescription;
031: import org.jboss.portal.wsrp.core.Property;
032: import org.jboss.portal.wsrp.core.PropertyDescription;
033: import org.jboss.portal.wsrp.core.RegistrationContext;
034: import org.jboss.portal.wsrp.core.RegistrationData;
035: import org.jboss.portal.wsrp.core.ServiceDescription;
036: import org.jboss.portal.wsrp.registration.RegistrationPropertyDescription;
037:
038: import java.util.ArrayList;
039: import java.util.Collections;
040: import java.util.HashMap;
041: import java.util.HashSet;
042: import java.util.List;
043: import java.util.Locale;
044: import java.util.Map;
045: import java.util.Set;
046:
047: /**
048: * @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
049: * @version $Revision: 9865 $
050: * @since 2.6
051: */
052: public class RegistrationInfo {
053: private static final Logger log = Logger
054: .getLogger(RegistrationInfo.class);
055:
056: private Long key;
057: private String persistentConsumerName;
058: private String persistentRegistrationHandle;
059: private byte[] persistentRegistrationState;
060: private Map persistentRegistrationProperties;
061:
062: private transient Boolean requiresRegistration;
063: private transient Boolean consistentWithProducerExpectations;
064: private transient RegistrationData registrationData;
065: private transient boolean dirty;
066: private transient ProducerInfo parent;
067:
068: public RegistrationInfo(ProducerInfo producerInfo) {
069: this ();
070: ParameterValidation.throwIllegalArgExceptionIfNull(
071: producerInfo, "ProducerInfo");
072: producerInfo.setRegistrationInfo(this );
073: parent = producerInfo;
074: }
075:
076: public RegistrationInfo(ProducerInfo producerInfo,
077: boolean requiresRegistration) {
078: this (producerInfo);
079: this .requiresRegistration = requiresRegistration;
080: }
081:
082: public RegistrationInfo() {
083: persistentConsumerName = WSRPConstants.DEFAULT_CONSUMER_NAME;
084: }
085:
086: public RegistrationInfo(RegistrationInfo other) {
087: ParameterValidation.throwIllegalArgExceptionIfNull(other,
088: "RegistrationInfo to clone from");
089: this .persistentConsumerName = other.persistentConsumerName;
090: this .persistentRegistrationHandle = other.persistentRegistrationHandle;
091:
092: if (other.persistentRegistrationState != null) {
093: this .persistentRegistrationState = new byte[other.persistentRegistrationState.length];
094: System.arraycopy(other.persistentRegistrationState, 0,
095: this .persistentRegistrationState, 0,
096: other.persistentRegistrationState.length);
097: }
098:
099: if (other.persistentRegistrationProperties != null) {
100: this .persistentRegistrationProperties = new HashMap(
101: other.persistentRegistrationProperties.size());
102: for (Object o : other.persistentRegistrationProperties
103: .values()) {
104: RegistrationProperty otherProp = (RegistrationProperty) o;
105: String name = otherProp.getName();
106: RegistrationProperty prop = new RegistrationProperty(
107: name, otherProp.getValue(), otherProp.getLang());
108: prop.setStatus(otherProp.getStatus());
109: this .persistentRegistrationProperties.put(name, prop);
110: }
111: }
112: }
113:
114: public Long getKey() {
115: return key;
116: }
117:
118: public void setKey(Long key) {
119: this .key = key;
120: }
121:
122: public String getRegistrationHandle() {
123: return persistentRegistrationHandle;
124: }
125:
126: public void setRegistrationHandle(String registrationHandle) {
127: this .persistentRegistrationHandle = modifyIfNeeded(
128: this .persistentRegistrationHandle, registrationHandle);
129: }
130:
131: public byte[] getRegistrationState() {
132: return persistentRegistrationState;
133: }
134:
135: public void setRegistrationState(byte[] registrationState) {
136: this .persistentRegistrationState = registrationState;
137: }
138:
139: public ProducerInfo getParent() {
140: return parent;
141: }
142:
143: public void setParent(ProducerInfo parent) {
144: this .parent = parent;
145: }
146:
147: public boolean isRefreshNeeded() {
148: boolean result = requiresRegistration == null || isModified();
149: if (result) {
150: log.debug("Refresh needed");
151: }
152: return result;
153: }
154:
155: public Boolean isRegistrationValid() {
156: if (consistentWithProducerExpectations == null
157: || requiresRegistration == null) {
158: return null;
159: }
160: return consistentWithProducerExpectations
161: && hasRegisteredIfNeeded();
162: }
163:
164: private boolean hasRegisteredIfNeeded() {
165: return (persistentRegistrationHandle != null && isRegistrationDeterminedRequired())
166: || isRegistrationDeterminedNotRequired();
167: }
168:
169: public Boolean isConsistentWithProducerExpectations() {
170: return consistentWithProducerExpectations;
171: }
172:
173: /**
174: * Determines whether the associated Producer requires registration.
175: *
176: * @return <code>null</code> if this RegistrationInfo hasn't queried the Producer yet and thus, doesn't have a
177: * definitive answer on whether or not the associated Producer requires registration,
178: * <code>Boolean.TRUE</code> if the associated Producer requires registration, <code>Boolean.FALSE</code>
179: * otherwise.
180: */
181: public Boolean isRegistrationRequired() {
182: return requiresRegistration;
183: }
184:
185: /**
186: * Determines whether it has been determined after querying the associated Producer that it requires registration.
187: *
188: * @return <code>true</code> if and only if the associated Producer has been queried and mandates registration,
189: * <code>false</code> otherwise.
190: * @throws IllegalStateException if {@link #refresh} has not yet been called
191: */
192: public boolean isRegistrationDeterminedRequired() {
193: if (requiresRegistration == null) {
194: throw new IllegalStateException(
195: "Registration status not yet known: call refresh first!");
196: }
197:
198: return requiresRegistration;
199: }
200:
201: /**
202: * Determines whether it has been determined after querying the associated Producer that it does <strong>NOT</strong>
203: * require registration.
204: *
205: * @return <code>true</code> if and only if the associated Producer has been queried and does NOT mandate
206: * registration, <code>false</code> otherwise.
207: * @throws IllegalStateException if {@link #refresh} has not yet been called
208: */
209: public boolean isRegistrationDeterminedNotRequired() {
210: if (requiresRegistration == null) {
211: throw new IllegalStateException(
212: "Registration status not yet known: call refresh first!");
213: }
214:
215: return !requiresRegistration;
216: }
217:
218: public boolean hasLocalInfo() {
219: return persistentRegistrationHandle != null
220: || !getRegistrationProperties().isEmpty();
221: }
222:
223: public RegistrationData getRegistrationData() {
224: registrationData = WSRPTypeFactory
225: .createDefaultRegistrationData();
226: registrationData.setConsumerName(persistentConsumerName);
227: List<Property> properties = new ArrayList<Property>();
228: Map regProps = getRegistrationProperties();
229: if (!regProps.isEmpty()) {
230: for (Object o : regProps.values()) {
231: RegistrationProperty prop = (RegistrationProperty) o;
232: String value = prop.getValue();
233: if (value != null && !prop.isDeterminedInvalid()) {
234: properties.add(WSRPTypeFactory
235: .createProperty(prop.getName(), prop
236: .getLang(), prop.getValue()));
237: }
238: }
239:
240: registrationData.setRegistrationProperties(properties
241: .toArray(new Property[regProps.size()]));
242: }
243:
244: return registrationData;
245: }
246:
247: public String getConsumerName() {
248: return persistentConsumerName;
249: }
250:
251: public void setConsumerName(String consumerName) {
252: this .persistentConsumerName = consumerName;
253: }
254:
255: public String getConsumerAgent() {
256: return WSRPConstants.CONSUMER_AGENT;
257: }
258:
259: public RegistrationProperty getRegistrationProperty(String name) {
260: ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(name,
261: "registration property name",
262: "RegistrationInfo.getRegistrationProperty");
263: return (RegistrationProperty) getRegistrationProperties().get(
264: name);
265: }
266:
267: public RegistrationProperty setRegistrationPropertyValue(
268: String name, String value) {
269: ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(name,
270: "registration property name",
271: "RegistrationInfo.setRegistrationPropertyValue");
272: ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(
273: value, "registration property value",
274: "RegistrationInfo.setRegistrationPropertyValue");
275:
276: RegistrationProperty prop = (RegistrationProperty) getOrCreateRegistrationPropertiesMap(
277: true).get(name);
278: if (prop != null) {
279: value = modifyIfNeeded(prop.getValue(), value);
280: prop.setValue(value);
281: } else {
282: // todo: deal with language more appropriately
283: prop = new RegistrationProperty(name, value, WSRPUtils
284: .toString(Locale.getDefault()));
285: getOrCreateRegistrationPropertiesMap(false).put(name, prop);
286: dirty = true;
287: }
288:
289: return prop;
290: }
291:
292: public void removeRegistrationProperty(String name) {
293: ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(name,
294: "registration property name",
295: "RegistrationInfo.removeRegistrationProperty");
296: Map propertiesMap = getOrCreateRegistrationPropertiesMap(false);
297: if (propertiesMap == null || propertiesMap.remove(name) == null) {
298: throw new IllegalArgumentException(
299: "Cannot remove inexistent registration property '"
300: + name + "'");
301: }
302: dirty = true;
303: }
304:
305: private Map getOrCreateRegistrationPropertiesMap(boolean forceCreate) {
306: if (forceCreate && persistentRegistrationProperties == null) {
307: persistentRegistrationProperties = new HashMap();
308: }
309:
310: return persistentRegistrationProperties;
311: }
312:
313: public Map getRegistrationProperties() {
314: Map properties = getOrCreateRegistrationPropertiesMap(false);
315: if (properties != null) {
316: return Collections.unmodifiableMap(properties);
317: } else {
318: return Collections.EMPTY_MAP;
319: }
320: }
321:
322: public void setRegistrationProperties(Map registrationProperties) {
323: this .persistentRegistrationProperties = registrationProperties;
324: }
325:
326: public Set getRegistrationPropertyNames() {
327: return getRegistrationProperties().keySet();
328: }
329:
330: /**
331: * @param serviceDescription
332: * @param producerId
333: * @param mergeWithLocalInfo
334: * @param forceRefresh
335: * @param forceCheckOfExtraProps
336: * @return
337: */
338: public RegistrationRefreshResult refresh(
339: ServiceDescription serviceDescription, String producerId,
340: boolean mergeWithLocalInfo, boolean forceRefresh,
341: boolean forceCheckOfExtraProps) {
342: log.debug("RegistrationInfo refresh requested");
343:
344: if (forceRefresh || isRefreshNeeded()) {
345: if (serviceDescription == null && parent != null) {
346: try {
347: serviceDescription = parent
348: .getServiceDescription(true);
349: } catch (PortletInvokerException e) {
350: log.debug(e);
351: serviceDescription = null;
352: }
353: }
354:
355: if (serviceDescription == null) {
356: String msg = "Couldn't get a service description to refresh from!";
357: log.debug(msg);
358: throw new IllegalArgumentException(msg);
359: }
360:
361: persistentRegistrationProperties = getOrCreateRegistrationPropertiesMap(true);
362:
363: RegistrationRefreshResult result = new RegistrationRefreshResult();
364: // if we're not merging, we need to copy the properties so that we can collect validation results.
365: if (!mergeWithLocalInfo) {
366: result.setRegistrationProperties(new HashMap(
367: persistentRegistrationProperties));
368: }
369: dirty = false;
370:
371: if (serviceDescription.isRequiresRegistration()) {
372: requiresRegistration = Boolean.TRUE;
373: StringBuffer message = new StringBuffer("Producer '")
374: .append(producerId).append(
375: "' requires registration");
376: result.appendToStatus(message.toString());
377: log.debug(message);
378:
379: // check if the configured registration properties match the producer expectations
380: ModelDescription regPropDescs = serviceDescription
381: .getRegistrationPropertyDescription();
382: if (regPropDescs != null) {
383: result.setHasIssues(false);
384: PropertyDescription[] propertyDescriptions = regPropDescs
385: .getPropertyDescriptions();
386: if (propertyDescriptions != null
387: && propertyDescriptions.length > 0) {
388: Map descriptionsMap = getRegistrationPropertyDescriptionsFromWSRP(propertyDescriptions);
389:
390: // check that we don't have unexpected registration properties and if so, mark them as invalid or remove them
391: Set expectedNames = descriptionsMap.keySet();
392: checkForExtraProperties(producerId, result,
393: expectedNames,
394: persistentRegistrationProperties,
395: !mergeWithLocalInfo);
396:
397: // Merge existing properties
398: for (Object o : descriptionsMap.values()) {
399: RegistrationProperty prop = (RegistrationProperty) o;
400: String name = prop.getName();
401: RegistrationProperty existing = getRegistrationProperty(name);
402: if (existing != null) {
403: // take the opportunity to add the property description... ^_^
404: existing.setDescription(prop
405: .getDescription());
406: if (existing.isDeterminedInvalid()) {
407: result.setHasIssues(true);
408: }
409: } else {
410: if (mergeWithLocalInfo) {
411: persistentRegistrationProperties
412: .put(name, prop);
413: } else {
414: prop
415: .setStatus(RegistrationProperty.MISSING_STATUS);
416: result.getRegistrationProperties()
417: .put(name, prop);
418: }
419:
420: result
421: .appendToStatus("Missing value for property '"
422: + name + "'");
423: result.setHasIssues(true);
424: }
425: }
426: } else {
427: handleNoRequiredRegistrationProperties(
428: producerId, result,
429: !mergeWithLocalInfo,
430: forceCheckOfExtraProps);
431: }
432: } else {
433: handleNoRequiredRegistrationProperties(producerId,
434: result, !mergeWithLocalInfo,
435: forceCheckOfExtraProps);
436: }
437: } else {
438: String msg = "Producer '" + producerId
439: + "' doesn't require registration";
440: log.debug(msg);
441: result.appendToStatus(msg);
442: requiresRegistration = Boolean.FALSE;
443: result.setHasIssues(false);
444: }
445:
446: // if we're merging, the resulting properties are the saved properties
447: if (mergeWithLocalInfo) {
448: result
449: .setRegistrationProperties(persistentRegistrationProperties);
450: }
451:
452: // if issues have been detected, mark the registration as invalid (but do not reset the data)
453: consistentWithProducerExpectations = !result.hasIssues();
454:
455: String msg = "Registration configuration is "
456: + (result.hasIssues() ? "NOT " : "") + "valid";
457: result.appendToStatus(msg);
458: log.debug(msg);
459: return result;
460: } else {
461: RegistrationRefreshResult result = new RegistrationRefreshResult();
462: result.setHasIssues(false);
463: result
464: .setRegistrationProperties(persistentRegistrationProperties);
465: return result;
466: }
467: }
468:
469: private void handleNoRequiredRegistrationProperties(
470: String producerId, RegistrationRefreshResult result,
471: boolean keepExtra, boolean forceCheckOfExtraProps) {
472: log
473: .debug("The producer didn't require any specific registration properties");
474: Map properties = getOrCreateRegistrationPropertiesMap(false);
475: if (properties != null && !properties.isEmpty()) {
476: if (forceCheckOfExtraProps || !hasRegisteredIfNeeded()) {
477: log
478: .debug("Registration data is available when none is expected by the producer");
479: checkForExtraProperties(producerId, result,
480: Collections.EMPTY_SET, properties, keepExtra);
481: } else {
482: log
483: .debug("Consumer is registered: producer most likely did not resend property descriptions");
484: result.setHasIssues(false);
485: }
486: } else {
487: log.debug("Using default registration data for producer '"
488: + producerId + "'");
489: registrationData = WSRPTypeFactory
490: .createDefaultRegistrationData();
491: result.setHasIssues(false);
492: }
493: }
494:
495: /**
496: * @param producerId
497: * @param result
498: * @param expectedNames
499: * @param properties
500: */
501: private void checkForExtraProperties(String producerId,
502: RegistrationRefreshResult result, Set expectedNames,
503: Map properties, boolean keepExtra) {
504: Set unexpected = new HashSet(properties.keySet());
505: unexpected.removeAll(expectedNames);
506: if (!unexpected.isEmpty()) {
507: StringBuffer message = new StringBuffer(
508: "Unexpected registration properties:\n");
509: int size = unexpected.size();
510: int index = 0;
511: for (Object anUnexpected : unexpected) {
512: String name = (String) anUnexpected;
513: message.append("'").append(name).append("'");
514: if (keepExtra) {
515: // mark the prop as invalid
516: RegistrationProperty prop = (RegistrationProperty) properties
517: .get(name);
518: prop.setInvalid(Boolean.TRUE,
519: RegistrationProperty.INEXISTENT_STATUS);
520:
521: // do the same in the result
522: prop = (RegistrationProperty) result
523: .getRegistrationProperties().get(name);
524: prop.setInvalid(Boolean.TRUE,
525: RegistrationProperty.INEXISTENT_STATUS);
526: } else {
527: message.append(" (was removed)");
528: properties.remove(name);
529: }
530:
531: if (index++ != size - 1) {
532: message.append(";");
533: }
534: }
535: log.debug(message);
536: result.appendToStatus(message.toString());
537: result.setHasIssues(true);
538: }
539: }
540:
541: /**
542: * @param descriptions
543: * @return
544: */
545: private Map getRegistrationPropertyDescriptionsFromWSRP(
546: PropertyDescription[] descriptions) {
547: if (descriptions != null) {
548: Map<String, RegistrationProperty> result = new HashMap<String, RegistrationProperty>(
549: descriptions.length);
550: for (PropertyDescription description : descriptions) {
551: String name = description.getName();
552: RegistrationPropertyDescription desc = WSRPUtils
553: .convertToRegistrationPropertyDescription(description);
554: RegistrationProperty prop = new RegistrationProperty(
555: name, null, WSRPUtils.toString(desc.getLang()));
556: prop.setDescription(desc);
557: prop.setInvalid(Boolean.TRUE,
558: RegistrationProperty.MISSING_VALUE_STATUS);
559: result.put(name, prop);
560: }
561:
562: return result;
563: } else {
564: return Collections.EMPTY_MAP;
565: }
566: }
567:
568: void resetRegistration() {
569: persistentRegistrationHandle = null;
570: persistentRegistrationState = null;
571: }
572:
573: public void setRegistrationContext(
574: RegistrationContext registrationContext) {
575: ParameterValidation.throwIllegalArgExceptionIfNull(
576: registrationContext, "RegistrationContext");
577: String handle = registrationContext.getRegistrationHandle();
578: ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(
579: handle, "registration handle", "RegistrationContext");
580: persistentRegistrationHandle = handle;
581: persistentRegistrationState = registrationContext
582: .getRegistrationState();
583: setRegistrationValidInternalState();
584: }
585:
586: /** todo: revert to package-only once the tests are moved to same package */
587: public void setRegistrationValidInternalState() {
588: // update RegistrationData if needed
589: getRegistrationData();
590:
591: // mark the registration properties as valid
592: if (persistentRegistrationProperties != null) {
593: for (Object o : persistentRegistrationProperties.values()) {
594: RegistrationProperty prop = (RegistrationProperty) o;
595: prop.setInvalid(Boolean.FALSE, null);
596: }
597: }
598:
599: consistentWithProducerExpectations = Boolean.TRUE; // since we have a registration context, we're consistent with the Producer
600: requiresRegistration = Boolean.TRUE; // we know we require registration
601: dirty = false; // our state is clean :)
602: }
603:
604: public RegistrationContext getRegistrationContext() {
605: if (persistentRegistrationHandle != null) {
606: RegistrationContext registrationContext = WSRPTypeFactory
607: .createRegistrationContext(persistentRegistrationHandle);
608: registrationContext
609: .setRegistrationState(persistentRegistrationState);
610: return registrationContext;
611: } else {
612: return null;
613: }
614: }
615:
616: public boolean isModified() {
617: return dirty;
618: }
619:
620: public void setModified(boolean modified) {
621: this .dirty = modified;
622: }
623:
624: public class RegistrationRefreshResult extends RefreshResult {
625: public RegistrationRefreshResult() {
626: super (false, "Unused");
627: }
628:
629: private Map registrationProperties;
630:
631: public Map getRegistrationProperties() {
632: return registrationProperties;
633: }
634:
635: public void setRegistrationProperties(Map registrationProperties) {
636: this .registrationProperties = registrationProperties;
637: }
638:
639: public boolean specificCode() {
640: throw new UnsupportedOperationException();
641: }
642:
643: public void setSpecificCode(boolean specificCode) {
644: throw new UnsupportedOperationException();
645: }
646: }
647:
648: private String modifyIfNeeded(String oldValue, String newValue) {
649: if ((oldValue != null && !oldValue.equals(newValue))
650: || (oldValue == null && newValue != null)) {
651: oldValue = newValue;
652: dirty = true;
653: consistentWithProducerExpectations = null;
654: }
655:
656: return oldValue;
657: }
658: }
|