001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2006, 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.portal.api.event.PortalEvent;
025: import org.jboss.portal.api.event.PortalEventContext;
026: import org.jboss.portal.common.invocation.InvocationException;
027: import org.jboss.portal.common.util.ParameterValidation;
028: import org.jboss.portal.common.value.StringValue;
029: import org.jboss.portal.jems.as.system.AbstractJBossService;
030: import org.jboss.portal.portlet.InvokerUnavailableException;
031: import org.jboss.portal.portlet.NoSuchPortletException;
032: import org.jboss.portal.portlet.Portlet;
033: import org.jboss.portal.portlet.PortletContext;
034: import org.jboss.portal.portlet.PortletInvokerException;
035: import org.jboss.portal.portlet.impl.spi.AbstractPortletInvocationContext;
036: import org.jboss.portal.portlet.invocation.ActionInvocation;
037: import org.jboss.portal.portlet.invocation.PortletInvocation;
038: import org.jboss.portal.portlet.invocation.RenderInvocation;
039: import org.jboss.portal.portlet.invocation.response.PortletInvocationResponse;
040: import org.jboss.portal.portlet.spi.UserContext;
041: import org.jboss.portal.portlet.state.DestroyCloneFailure;
042: import org.jboss.portal.portlet.state.PropertyChange;
043: import org.jboss.portal.portlet.state.PropertyMap;
044: import org.jboss.portal.portlet.state.SimplePropertyMap;
045: import org.jboss.portal.wsrp.UserContextConverter;
046: import org.jboss.portal.wsrp.WSRPConstants;
047: import org.jboss.portal.wsrp.WSRPConsumer;
048: import org.jboss.portal.wsrp.WSRPTypeFactory;
049: import org.jboss.portal.wsrp.WSRPUtils;
050: import org.jboss.portal.wsrp.consumer.portlet.WSRPPortlet;
051: import org.jboss.portal.wsrp.consumer.portlet.info.WSRPPortletInfo;
052: import org.jboss.portal.wsrp.core.ClonePortlet;
053: import org.jboss.portal.wsrp.core.DestroyFailed;
054: import org.jboss.portal.wsrp.core.DestroyPortlets;
055: import org.jboss.portal.wsrp.core.DestroyPortletsResponse;
056: import org.jboss.portal.wsrp.core.GetPortletProperties;
057: import org.jboss.portal.wsrp.core.Property;
058: import org.jboss.portal.wsrp.core.PropertyList;
059: import org.jboss.portal.wsrp.core.RegistrationContext;
060: import org.jboss.portal.wsrp.core.RegistrationData;
061: import org.jboss.portal.wsrp.core.ResetProperty;
062: import org.jboss.portal.wsrp.core.RuntimeContext;
063: import org.jboss.portal.wsrp.core.SetPortletProperties;
064: import org.jboss.portal.wsrp.core.WSRP_v1_Markup_PortType;
065: import org.jboss.portal.wsrp.core.WSRP_v1_PortletManagement_PortType;
066: import org.jboss.portal.wsrp.core.WSRP_v1_Registration_PortType;
067: import org.jboss.portal.wsrp.core.WSRP_v1_ServiceDescription_PortType;
068: import org.jboss.portal.wsrp.services.ServiceFactory;
069: import org.jboss.portal.wsrp.servlet.UserAccess;
070:
071: import javax.servlet.http.HttpServletRequest;
072: import javax.servlet.http.HttpSession;
073: import java.util.ArrayList;
074: import java.util.Collections;
075: import java.util.HashSet;
076: import java.util.LinkedHashSet;
077: import java.util.List;
078: import java.util.Locale;
079: import java.util.Map;
080: import java.util.Set;
081:
082: /**
083: * @author <a href="mailto:boleslaw.dawidowicz@jboss.org">Boleslaw Dawidowicz</a>
084: * @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
085: * @version $Revision: 9360 $
086: * @since 2.4
087: */
088: public class WSRPConsumerImpl extends AbstractJBossService implements
089: WSRPConsumer {
090: private ActionHandler actionHandler;
091: private RenderHandler renderHandler;
092: private SessionHandler sessionHandler;
093:
094: private ProducerInfo producerInfo;
095:
096: /** A registration data element used to indicate when no registration was required by the producer */
097: private final static RegistrationData REGISTRATION_NOT_NEEDED = WSRPTypeFactory
098: .createDefaultRegistrationData();
099:
100: static {
101: REGISTRATION_NOT_NEEDED.setConsumerAgent("INVALID AGENT");
102: REGISTRATION_NOT_NEEDED.setConsumerName("INVALID NAME");
103: REGISTRATION_NOT_NEEDED.setRegistrationProperties(null);
104: }
105:
106: /** The default user scopes as per the specification (6.1.4) */
107: private static final Set<String> WSRP_DEFAULT_USER_SCOPE = new HashSet<String>(
108: 2);
109:
110: static {
111: WSRP_DEFAULT_USER_SCOPE.add(WSRPConstants.CACHE_FOR_ALL);
112: WSRP_DEFAULT_USER_SCOPE.add(WSRPConstants.CACHE_PER_USER);
113: }
114:
115: /** The set of supported user scopes */
116: private Set supportedUserScopes = WSRP_DEFAULT_USER_SCOPE; // todo: make it possible to support different user scopes
117: private transient boolean started;
118:
119: public WSRPConsumerImpl() {
120: this (new ProducerInfo());
121: }
122:
123: WSRPConsumerImpl(ProducerInfo info) {
124: ParameterValidation.throwIllegalArgExceptionIfNull(info,
125: "ProducerInfo");
126:
127: producerInfo = info;
128: actionHandler = new ActionHandler(this );
129: renderHandler = new RenderHandler(this );
130: sessionHandler = new SessionHandler(this );
131: }
132:
133: public ProducerInfo getProducerInfo() {
134: return producerInfo;
135: }
136:
137: public void setProducerInfo(ProducerInfo producerInfo) {
138: this .producerInfo = producerInfo;
139: }
140:
141: // PortletInvoker implementation ************************************************************************************
142:
143: public Set getPortlets() throws InvokerUnavailableException {
144: try {
145: Map portletMap = producerInfo.getPortletMap();
146: return new LinkedHashSet(portletMap.values());
147: } catch (Exception e) {
148: throw new InvokerUnavailableException(e.getMessage(), e
149: .getCause());
150: }
151: }
152:
153: public Portlet getPortlet(PortletContext portletContext)
154: throws IllegalArgumentException, PortletInvokerException {
155: ParameterValidation.throwIllegalArgExceptionIfNull(
156: portletContext, "PortletContext");
157:
158: Portlet portlet = producerInfo.getPortlet(portletContext);
159:
160: if (portlet == null) {
161: throw new NoSuchPortletException(portletContext.getId());
162: } else {
163: return portlet;
164: }
165: }
166:
167: public PortletInvocationResponse invoke(PortletInvocation invocation)
168: throws PortletInvokerException {
169: InvocationHandler handler;
170:
171: if (invocation instanceof RenderInvocation) {
172: handler = renderHandler;
173: } else if (invocation instanceof ActionInvocation) {
174: handler = actionHandler;
175: } else {
176: throw new InvocationException("Unknown invocation type: "
177: + invocation);
178: }
179:
180: return handler.handle(invocation);
181: }
182:
183: public PortletContext createClone(PortletContext portletContext)
184: throws IllegalArgumentException, PortletInvokerException,
185: UnsupportedOperationException {
186: ParameterValidation.throwIllegalArgExceptionIfNull(
187: portletContext, "PortletContext");
188:
189: WSRPPortlet original = getWSRPPortlet(portletContext);
190: if (original == null) {
191: throw new PortletInvokerException("No portlet '"
192: + portletContext.getId() + "' to clone!");
193: }
194:
195: ClonePortlet clonePortlet = WSRPTypeFactory.createClonePortlet(
196: getRegistrationContext(), WSRPUtils
197: .convertToWSRPPortletContext(portletContext),
198: UserAccess.getUserContext());
199: try {
200: return WSRPUtils
201: .convertToPortalPortletContext(getPortletManagementService()
202: .clonePortlet(clonePortlet));
203: } catch (Exception e) {
204: throw new PortletInvokerException(
205: "Couldn't clone portlet '" + portletContext.getId()
206: + "'", e);
207: }
208: }
209:
210: public List destroyClones(List portletContexts)
211: throws IllegalArgumentException, PortletInvokerException,
212: UnsupportedOperationException {
213: ParameterValidation.throwIllegalArgExceptionIfNull(
214: portletContexts, "Portlet identifiers");
215:
216: int numberOfClones = portletContexts.size();
217: if (numberOfClones == 0) {
218: return Collections.EMPTY_LIST;
219: }
220:
221: List<String> handles = new ArrayList<String>(numberOfClones);
222: for (Object portletContext : portletContexts) {
223: PortletContext context = (PortletContext) portletContext;
224: String id = context.getId();
225: handles.add(id);
226: }
227: log.debug("Attempting to destroy clones: " + handles);
228:
229: DestroyPortlets destroyPortlets = WSRPTypeFactory
230: .createDestroyPortlets(getRegistrationContext(),
231: handles.toArray(new String[handles.size()]));
232:
233: try {
234: DestroyPortletsResponse response = getPortletManagementService()
235: .destroyPortlets(destroyPortlets);
236: DestroyFailed[] failures = response.getDestroyFailed();
237: List<DestroyCloneFailure> result = Collections.emptyList();
238: if (failures != null) {
239: int failureNumbers = failures.length;
240: result = new ArrayList<DestroyCloneFailure>(
241: failureNumbers);
242: // list all the failures and successes
243: for (int i = 0; i < failureNumbers; i++) {
244: DestroyFailed failure = failures[i];
245: String handle = failure.getPortletHandle();
246: result.add(new DestroyCloneFailure(handle, failure
247: .getReason()));
248: handles.remove(handle);
249: log
250: .debug("Couldn't destroy clone '" + handle
251: + "'");
252: }
253: }
254:
255: // update ProducerInfo's caches by removing all the successfully destroyed clones
256: if (!handles.isEmpty()) {
257: for (String handle : handles) {
258: producerInfo.removeHandleFromCaches(handle);
259: }
260: }
261:
262: return result;
263: } catch (Exception e) {
264: throw new PortletInvokerException(
265: "Couldn't destroy clones.", e);
266: }
267: }
268:
269: public PropertyMap getProperties(PortletContext portletContext,
270: Set keys) throws IllegalArgumentException,
271: PortletInvokerException, UnsupportedOperationException {
272: ParameterValidation.throwIllegalArgExceptionIfNull(keys,
273: "Portlet ids");
274:
275: return getProperties(portletContext, (String[]) keys
276: .toArray(new String[keys.size()]));
277: }
278:
279: private PropertyMap getProperties(PortletContext portletContext,
280: String[] keys) throws PortletInvokerException {
281: ParameterValidation.throwIllegalArgExceptionIfNull(
282: portletContext, "PortletContext");
283:
284: GetPortletProperties getPortletProperties = WSRPTypeFactory
285: .createGetPortletProperties(
286: getRegistrationContext(),
287: WSRPUtils
288: .convertToWSRPPortletContext(portletContext));
289: getPortletProperties.setNames(keys);
290:
291: try {
292: PropertyList response = getPortletManagementService()
293: .getPortletProperties(getPortletProperties);
294: Property[] props = response.getProperties();
295:
296: if (props != null) {
297: int propNumber = props.length;
298: PropertyMap result = new SimplePropertyMap(propNumber);
299:
300: for (int i = 0; i < propNumber; i++) {
301: Property prop = props[i];
302: String name = prop.getName();
303: String value = prop.getStringValue();
304: result.put(name, new StringValue(value)); // todo: is that all?!?
305: }
306:
307: return result;
308: } else {
309: return new SimplePropertyMap();
310: }
311: } catch (Exception e) {
312: // something went wrong but support for getPortletProperties is optional so return an empty PropertyMap
313: log.debug("Couldn't get properties for portlet '"
314: + portletContext.getId() + "'", e);
315: return new SimplePropertyMap();
316: }
317: }
318:
319: public PropertyMap getProperties(PortletContext portletContext)
320: throws IllegalArgumentException, PortletInvokerException,
321: UnsupportedOperationException {
322: return getProperties(portletContext, (String[]) null);
323: }
324:
325: public PortletContext setProperties(PortletContext portletContext,
326: PropertyChange[] changes) throws IllegalArgumentException,
327: PortletInvokerException, UnsupportedOperationException {
328: ParameterValidation.throwIllegalArgExceptionIfNull(
329: portletContext, "PortletContext");
330: ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(
331: changes, "Property changes");
332:
333: WSRPPortlet portlet = getWSRPPortlet(portletContext);
334: if (portlet == null) {
335: throw new PortletInvokerException(
336: "Cannot set properties on portlet '"
337: + portletContext.getId()
338: + "' because there is no such portlet.");
339: }
340:
341: PropertyList propertyList = WSRPTypeFactory
342: .createPropertyList();
343: int changesNumber = changes.length;
344: List<Property> updates = new ArrayList<Property>(changesNumber);
345: List<ResetProperty> resets = new ArrayList<ResetProperty>(
346: changesNumber);
347: for (int i = 0; i < changesNumber; i++) {
348: PropertyChange change = changes[i];
349: switch (change.getType()) {
350: case PropertyChange.PREF_RESET:
351: resets.add(WSRPTypeFactory.createResetProperty(change
352: .getKey()));
353: break;
354:
355: case PropertyChange.PREF_UPDATE:
356: // todo: deal with language more appropriately
357: updates.add(WSRPTypeFactory.createProperty(change
358: .getKey(), WSRPUtils.toString(Locale
359: .getDefault()), change.getValue().asString()));
360: break;
361:
362: default:
363: throw new IllegalArgumentException(
364: "Unexpected property change type: "
365: + change.getType());
366: }
367: }
368: propertyList.setProperties(updates.toArray(new Property[updates
369: .size()]));
370: propertyList.setResetProperties(resets
371: .toArray(new ResetProperty[resets.size()]));
372: SetPortletProperties setPortletProperties = WSRPTypeFactory
373: .createSetPortletProperties(
374: getRegistrationContext(),
375: WSRPUtils
376: .convertToWSRPPortletContext(portletContext),
377: propertyList);
378:
379: try {
380: PortletContext newPortletContext = WSRPUtils
381: .convertToPortalPortletContext(getPortletManagementService()
382: .setPortletProperties(setPortletProperties));
383: portlet.setPortletContext(newPortletContext);
384: return newPortletContext;
385: } catch (Exception e) {
386: throw new PortletInvokerException(
387: "Unable to set properties for portlet '"
388: + portletContext.getId() + "'", e);
389: }
390: }
391:
392: // Accessors ********************************************************************************************************
393:
394: public String getProducerId() {
395: return producerInfo.getId();
396: }
397:
398: public SessionHandler getSessionHandler() {
399: return sessionHandler;
400: }
401:
402: // Portlet-related methods ******************************************************************************************
403:
404: public Map<String, Set<Portlet>> getPortletGroupMap()
405: throws PortletInvokerException {
406: return producerInfo.getPortletGroupMap();
407: }
408:
409: /**
410: * @param invocation
411: * @return
412: * @since 2.6
413: */
414: static PortletContext getPortletContext(PortletInvocation invocation) {
415: return invocation.getTarget();
416: }
417:
418: WSRPPortletInfo getPortletInfo(PortletInvocation invocation)
419: throws PortletInvokerException {
420: return (WSRPPortletInfo) getWSRPPortlet(
421: getPortletContext(invocation)).getInfo();
422: }
423:
424: WSRPPortlet getWSRPPortlet(PortletContext portletContext)
425: throws PortletInvokerException {
426: return (WSRPPortlet) getPortlet(portletContext);
427: }
428:
429: public Set getSupportedUserScopes() {
430: return Collections.unmodifiableSet(supportedUserScopes);
431: }
432:
433: /**
434: * Determines whether the specified user scope (for markup caching) is supported.
435: *
436: * @param userScope the user scope which support is to be determined
437: * @return <code>true</code> if the given user scope is supported, <code>false</code> otherwise
438: */
439: public boolean supportsUserScope(String userScope) {
440: return supportedUserScopes.contains(userScope);
441: }
442:
443: // Registration *****************************************************************************************************
444:
445: void handleInvalidRegistrationFault()
446: throws PortletInvokerException {
447: // reset registration data and try again
448: producerInfo.resetRegistration();
449: }
450:
451: RegistrationContext getRegistrationContext()
452: throws PortletInvokerException {
453: return producerInfo.getRegistrationContext();
454: }
455:
456: // Session information access ***************************************************************************************
457:
458: public ProducerSessionInformation getProducerSessionInformationFrom(
459: PortletInvocation invocation) {
460: return sessionHandler.getProducerSessionInformation(invocation);
461: }
462:
463: public ProducerSessionInformation getProducerSessionInformationFrom(
464: HttpSession session) {
465: return sessionHandler.getProducerSessionInformation(session);
466: }
467:
468: public void activate() throws Exception {
469: internalStart();
470: producerInfo.setActiveAndSave(getEndpointConfigurationInfo()
471: .isAvailable());
472: }
473:
474: private void internalStart() throws Exception {
475: if (!started) {
476: try {
477: start();
478: } catch (Exception e) {
479: // mark the consumer as inactive if it cannot be started
480: producerInfo.setActiveAndSave(false);
481: throw e;
482: }
483:
484: log.info("Consumer with id '" + getProducerId()
485: + "' activated");
486: }
487: }
488:
489: public void deactivate() throws Exception {
490: producerInfo.setActiveAndSave(false);
491: if (started) {
492: stop();
493: log.info("Consumer with id '" + getProducerId()
494: + "' deactivated");
495: }
496: }
497:
498: public boolean isActive() {
499: return producerInfo.isActive() && started;
500: }
501:
502: public boolean isRefreshNeeded() {
503: return !started || producerInfo.isRefreshNeeded(false);
504: }
505:
506: public RefreshResult refresh(boolean forceRefresh)
507: throws PortletInvokerException {
508: try {
509: internalStart();
510: } catch (Exception e) {
511: throw new PortletInvokerException(e);
512: }
513:
514: return producerInfo.detailedRefresh(forceRefresh);
515: }
516:
517: // Service implementation *******************************************************************************************
518:
519: protected void startService() throws Exception {
520: super .startService();
521: getEndpointConfigurationInfo().start();
522: started = true;
523: log.info("Consumer with id '" + getProducerId() + "' started");
524: }
525:
526: protected void stopService() throws Exception {
527: getEndpointConfigurationInfo().stop();
528: super .stopService();
529: started = false;
530: log.info("Consumer with id '" + getProducerId() + "' stopped");
531: }
532:
533: // Web services access **********************************************************************************************
534:
535: /**
536: * Needed to wire Consumer tests.
537: *
538: * @param serviceFactory
539: */
540: public void setServiceFactory(ServiceFactory serviceFactory) {
541: getEndpointConfigurationInfo()
542: .setServiceFactory(serviceFactory);
543: }
544:
545: private EndpointConfigurationInfo getEndpointConfigurationInfo() {
546: return producerInfo.getEndpointConfigurationInfo();
547: }
548:
549: public WSRP_v1_ServiceDescription_PortType getServiceDescriptionService()
550: throws PortletInvokerException {
551: producerInfo.refresh(false);
552: return getEndpointConfigurationInfo()
553: .getServiceDescriptionService();
554: }
555:
556: public WSRP_v1_Markup_PortType getMarkupService()
557: throws PortletInvokerException {
558: producerInfo.refresh(false);
559: return getEndpointConfigurationInfo().getMarkupService();
560: }
561:
562: public WSRP_v1_PortletManagement_PortType getPortletManagementService()
563: throws PortletInvokerException {
564: producerInfo.refresh(false);
565: return getEndpointConfigurationInfo()
566: .getPortletManagementService();
567: }
568:
569: public WSRP_v1_Registration_PortType getRegistrationService()
570: throws PortletInvokerException {
571: producerInfo.refresh(false);
572: return getEndpointConfigurationInfo().getRegistrationService();
573: }
574:
575: public void refreshProducerInfo() throws PortletInvokerException {
576: producerInfo.refresh(true);
577: sessionHandler.setRequiresInitCookie(producerInfo
578: .getRequiresInitCookie());
579: }
580:
581: public void releaseSessions() throws PortletInvokerException {
582: sessionHandler.releaseSessions();
583: }
584:
585: // Support methods **************************************************************************************************
586:
587: private String getUserContextKeyFor(UserContext userContext) {
588: // fix-me: probably need to have an Id attribute on userId context.
589: String userId = userContext.getId();
590: if (userId != null) {
591: return userId;
592: } else {
593: return null;
594: }
595: }
596:
597: // fix-me!
598: org.jboss.portal.wsrp.core.UserContext getUserContextFrom(
599: PortletInvocation invocation, RuntimeContext runtimeContext)
600: throws PortletInvokerException {
601: // first decide if we need to pass the user context...
602: WSRPPortletInfo info = getPortletInfo(invocation);
603:
604: if (info != null && info.isUserContextStoredInSession()
605: && runtimeContext.getSessionID() != null) {
606: return null; // the user context is most likely in the session already
607: }
608:
609: // todo: deal with user categories and user context key properly
610: UserContext userContext = invocation.getUserContext();
611: if (userContext != null) {
612: String userContextKey = getUserContextKeyFor(userContext);
613: if (userContextKey == null) {
614: return null;
615: }
616:
617: return UserContextConverter.createWSRPUserContextFrom(
618: userContext, userContextKey, null);
619: }
620: return null;
621: }
622:
623: void setTemplatesIfNeeded(PortletInvocation invocation,
624: RuntimeContext runtimeContext)
625: throws PortletInvokerException {
626: // todo: could store templates in producer session info to avoid to re-generate them all the time?
627: WSRPPortletInfo info = getPortletInfo(invocation);
628:
629: if (info != null
630: && info.isDoesUrlTemplateProcessing()
631: && (!info.isTemplatesStoredInSession() || runtimeContext
632: .getSessionID() == null)) {
633: // we need to supply the templates since the portlet does URL processing and either doesn't store
634: // templates in the session or no session has been established yet
635: runtimeContext.setTemplates(WSRPTypeFactory
636: .createTemplates(invocation.getPortletContext()));
637: }
638: }
639:
640: static HttpServletRequest getHttpRequest(
641: PortletInvocation invocation) {
642: AbstractPortletInvocationContext invocationContext = (AbstractPortletInvocationContext) invocation
643: .getContext();
644: return invocationContext.getClientRequest();
645: }
646:
647: static HttpSession getHttpSession(PortletInvocation invocation) {
648: return getHttpRequest(invocation).getSession();
649: }
650:
651: /**
652: * Just delegate to SessionHandler
653: *
654: * @param eventContext
655: * @param event
656: * @since 2.6
657: */
658: public void onEvent(PortalEventContext eventContext,
659: PortalEvent event) {
660: sessionHandler.onEvent(eventContext, event);
661: }
662: }
|