001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.components.portletentity;
018:
019: import java.io.IOException;
020: import java.security.Principal;
021: import java.util.Arrays;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.ArrayList;
026: import java.util.Locale;
027: import java.util.Map;
028: import java.util.Set;
029: import java.util.prefs.BackingStoreException;
030: import java.util.prefs.Preferences;
031:
032: import javax.portlet.PortletMode;
033:
034: import org.apache.jetspeed.JetspeedActions;
035: import org.apache.jetspeed.aggregator.RenderTrackable;
036: import org.apache.jetspeed.components.persistence.store.PersistenceStore;
037: import org.apache.jetspeed.components.persistence.store.PersistenceStoreRuntimeExcpetion;
038: import org.apache.jetspeed.components.persistence.store.RemovalAware;
039: import org.apache.jetspeed.components.portletregistry.PortletRegistry;
040: import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
041: import org.apache.jetspeed.om.common.portlet.MutablePortletEntity;
042: import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
043: import org.apache.jetspeed.om.common.portlet.PrincipalAware;
044: import org.apache.jetspeed.om.page.Fragment;
045: import org.apache.jetspeed.om.portlet.impl.FragmentPortletDefinition;
046: import org.apache.jetspeed.om.preference.FragmentPreference;
047: import org.apache.jetspeed.om.preference.impl.PrefsPreference;
048: import org.apache.jetspeed.om.preference.impl.PrefsPreferenceSetImpl;
049: import org.apache.jetspeed.om.window.impl.PortletWindowListImpl;
050: import org.apache.jetspeed.page.PageManager;
051: import org.apache.jetspeed.request.RequestContext;
052: import org.apache.jetspeed.request.RequestContextComponent;
053: import org.apache.jetspeed.util.JetspeedObjectID;
054: import org.apache.pluto.om.common.Description;
055: import org.apache.pluto.om.common.ObjectID;
056: import org.apache.pluto.om.common.Preference;
057: import org.apache.pluto.om.common.PreferenceSet;
058: import org.apache.pluto.om.entity.PortletApplicationEntity;
059: import org.apache.pluto.om.portlet.PortletDefinition;
060: import org.apache.pluto.om.window.PortletWindow;
061: import org.apache.pluto.om.window.PortletWindowList;
062: import org.apache.pluto.util.StringUtils;
063:
064: /**
065: * Portlet Entity default implementation.
066: *
067: * @author <a href="mailto:taylor@apache.org">David Sean Taylor </a>
068: * @author <a href="mailto:weaver@apache.org">Scott T. Weaver </a>
069: * @version $Id: PortletEntityImpl.java,v 1.9 2005/04/29 13:59:08 weaver Exp $
070: */
071: public class PortletEntityImpl implements MutablePortletEntity,
072: PrincipalAware, RemovalAware, RenderTrackable {
073: private long oid;
074: private JetspeedObjectID id;
075: protected static PortletEntityAccessComponent pac;
076: protected static PortletRegistry registry;
077: protected static RequestContextComponent rcc;
078: protected static PageManager pm;
079:
080: protected PrefsPreferenceSetImpl pagePreferenceSet;
081: protected Map perPrincipalPrefs = new HashMap();
082: protected Map originalValues;
083: private PortletApplicationEntity applicationEntity = null;
084: private PortletWindowList portletWindows = new PortletWindowListImpl();
085: private PortletDefinitionComposite portletDefinition = null;
086: protected String portletName;
087: protected String appName;
088: private boolean dirty = false;
089: private Fragment fragment;
090: private ThreadLocal fragmentPortletDefinition = new ThreadLocal();
091:
092: protected transient int timeoutCount = 0;
093: protected transient long expiration = 0;
094:
095: public PortletEntityImpl(Fragment fragment) {
096: setFragment(fragment);
097: }
098:
099: public PortletEntityImpl() {
100: super ();
101: }
102:
103: public static final String NO_PRINCIPAL = "no-principal";
104: public static final String ENTITY_DEFAULT_PRINCIPAL = "entity-default";
105:
106: public ObjectID getId() {
107: return id;
108: }
109:
110: public long getOid() {
111: return oid;
112: }
113:
114: public void setId(String id) {
115: this .id = JetspeedObjectID.createFromString(id);
116: }
117:
118: /**
119: *
120: * <p>
121: * getPreferenceSet
122: * </p>
123: *
124: * @see org.apache.pluto.om.entity.PortletEntity#getPreferenceSet()
125: * @return
126: */
127: public PreferenceSet getPreferenceSet() {
128: if (isEditDefaultsMode()) {
129: return getPreferenceSetFromPage();
130: } else {
131: Principal currentUser = getPrincipal();
132: return getPreferenceSet(currentUser);
133: }
134: }
135:
136: public PreferenceSet getPreferenceSet(Principal principal) {
137: PrefsPreferenceSetImpl preferenceSet = (PrefsPreferenceSetImpl) perPrincipalPrefs
138: .get(principal);
139: try {
140: if (preferenceSet == null || !dirty) {
141: String prefNodePath = MutablePortletEntity.PORTLET_ENTITY_ROOT
142: + "/"
143: + getId()
144: + "/"
145: + principal.getName()
146: + "/"
147: + PrefsPreference.PORTLET_PREFERENCES_ROOT;
148: Preferences prefNode = Preferences.userRoot().node(
149: prefNodePath);
150: preferenceSet = new PrefsPreferenceSetImpl(prefNode);
151: perPrincipalPrefs.put(principal, preferenceSet);
152: if (pac.isMergeSharedPreferences()) {
153: mergePreferencesSet(preferenceSet);
154: }
155: backupValues(preferenceSet);
156: dirty = true;
157: }
158: } catch (BackingStoreException e) {
159: String msg = "Preference backing store failed: "
160: + e.toString();
161: IllegalStateException ise = new IllegalStateException(msg);
162: ise.initCause(e);
163: throw ise;
164: }
165: return preferenceSet;
166: }
167:
168: private PreferenceSet getPreferenceSetFromPage() {
169: PrefsPreferenceSetImpl preferenceSet = this .pagePreferenceSet;
170:
171: try {
172: if (preferenceSet == null || !dirty) {
173: String prefNodePath = MutablePortletEntity.PORTLET_ENTITY_ROOT
174: + "/"
175: + getId()
176: + "/"
177: + PrefsPreference.PORTLET_PREFERENCES_ROOT;
178:
179: Preferences prefNode = Preferences.systemRoot().node(
180: prefNodePath);
181: preferenceSet = new PrefsPreferenceSetImpl(prefNode);
182: this .pagePreferenceSet = preferenceSet;
183:
184: List fragmentPreferences = this .fragment
185: .getPreferences();
186:
187: if (fragmentPreferences != null) {
188: for (Iterator it = fragmentPreferences.iterator(); it
189: .hasNext();) {
190: FragmentPreference preference = (FragmentPreference) it
191: .next();
192: List preferenceValues = preference
193: .getValueList();
194: preferenceSet.add(preference.getName(),
195: preferenceValues);
196: }
197: }
198:
199: backupValues(preferenceSet);
200: dirty = true;
201: }
202: } catch (BackingStoreException e) {
203: String msg = "Preference backing store failed: "
204: + e.toString();
205: IllegalStateException ise = new IllegalStateException(msg);
206: ise.initCause(e);
207: throw ise;
208: }
209: return preferenceSet;
210: }
211:
212: private void mergePreferencesSet(PrefsPreferenceSetImpl userPrefSet)
213: throws BackingStoreException {
214: String sharedNodePath = MutablePortletEntity.PORTLET_ENTITY_ROOT
215: + "/"
216: + getId()
217: + "/"
218: + NO_PRINCIPAL
219: + "/"
220: + PrefsPreference.PORTLET_PREFERENCES_ROOT;
221: Preferences sharedNode = Preferences.userRoot().node(
222: sharedNodePath);
223: if (sharedNode == null)
224: return;
225: PrefsPreferenceSetImpl sharedSet = new PrefsPreferenceSetImpl(
226: sharedNode);
227: if (sharedSet.size() == 0)
228: return;
229: Set names = userPrefSet.getNames();
230: Iterator sharedPrefs = sharedSet.iterator();
231: int index = 0;
232: while (sharedPrefs.hasNext()) {
233: PrefsPreference sharedPref = (PrefsPreference) sharedPrefs
234: .next();
235: // this seems limiting, removing if (names.contains(sharedPref.getName()))
236: List prefs = Arrays.asList(sharedPref.getValueArray());
237: userPrefSet.add(sharedPref.getName(), prefs);
238: index++;
239: }
240: }
241:
242: /**
243: * <p>
244: * backupValues
245: * </p>
246: *
247: *
248: */
249: protected void backupValues(PreferenceSet preferenceSet) {
250: originalValues = new HashMap();
251: Iterator itr = preferenceSet.iterator();
252: while (itr.hasNext()) {
253: PrefsPreference pref = (PrefsPreference) itr.next();
254:
255: String[] currentValues = pref.getValueArray();
256: String[] backUp = new String[currentValues.length];
257: System.arraycopy(currentValues, 0, backUp, 0,
258: currentValues.length);
259: originalValues.put(pref.getName(), backUp);
260:
261: }
262: }
263:
264: public PortletDefinition getPortletDefinition() {
265: // there are cases when jetspeed gets initialized before
266: // all of the portlet web apps have. In this event, premature
267: // access to the portal would cause portlet entities to be cached
268: // with their associated window without there corresponding PortletDefinition
269: // (becuase the PortletApplication has yet to be registered).
270: if (this .portletDefinition == null) {
271: PortletDefinition pd = registry
272: .getPortletDefinitionByIdentifier(getPortletUniqueName());
273: if (pd != null) {
274: // only store a really found PortletDefinition
275: // to prevent an IllegalArgumentException to be thrown
276: setPortletDefinition(pd);
277: } else {
278: return null;
279: }
280: }
281:
282: // Wrap the portlet defintion every request thread
283: PortletDefinition fpd = (PortletDefinition) fragmentPortletDefinition
284: .get();
285: if (fpd == null) {
286: fpd = new FragmentPortletDefinition(this .portletDefinition,
287: fragment);
288: fragmentPortletDefinition.set(fpd);
289: }
290: return fpd;
291: }
292:
293: public PortletApplicationEntity getPortletApplicationEntity() {
294: return applicationEntity;
295: }
296:
297: public PortletWindowList getPortletWindowList() {
298: return portletWindows;
299: }
300:
301: /**
302: *
303: * <p>
304: * store
305: * </p>
306: *
307: */
308: public void store() throws IOException {
309: if (isEditDefaultsMode()) {
310: storeToPage();
311: } else {
312: Principal currentUser = getPrincipal();
313: store(currentUser);
314: }
315: }
316:
317: public void store(Principal principal) throws IOException {
318: if (pac == null) {
319: throw new IllegalStateException(
320: "You must call PortletEntityImpl.setPorteltEntityDao() before "
321: + "invoking PortletEntityImpl.store().");
322: }
323:
324: PreferenceSet preferenceSet = (PreferenceSet) perPrincipalPrefs
325: .get(principal);
326: pac.storePreferenceSet(preferenceSet, this );
327: dirty = false;
328: if (preferenceSet != null) {
329: backupValues(preferenceSet);
330: }
331: }
332:
333: private void storeToPage() throws IOException {
334: if (pm == null) {
335: throw new IllegalStateException(
336: "You must set pageManager before "
337: + "invoking PortletEntityImpl.store().");
338: }
339:
340: PreferenceSet preferenceSet = this .pagePreferenceSet;
341: List preferences = new ArrayList();
342:
343: for (Iterator it = preferenceSet.iterator(); it.hasNext();) {
344: Preference pref = (Preference) it.next();
345:
346: FragmentPreference preference = pm.newFragmentPreference();
347: preference.setName(pref.getName());
348: List preferenceValues = new ArrayList();
349:
350: for (Iterator iterVals = pref.getValues(); iterVals
351: .hasNext();) {
352: preferenceValues.add(iterVals.next());
353: }
354:
355: preference.setValueList(preferenceValues);
356: preferences.add(preference);
357: }
358:
359: this .fragment.setPreferences(preferences);
360:
361: try {
362: pm.updatePage(rcc.getRequestContext().getPage());
363: } catch (Exception e) {
364: }
365:
366: dirty = false;
367: if (preferenceSet != null) {
368: backupValues(preferenceSet);
369: }
370: }
371:
372: /**
373: *
374: * <p>
375: * reset
376: * </p>
377: *
378: */
379:
380: public void reset() throws IOException {
381: PrefsPreferenceSetImpl preferenceSet = (PrefsPreferenceSetImpl) perPrincipalPrefs
382: .get(getPrincipal());
383: try {
384: if (originalValues != null && preferenceSet != null) {
385: Iterator prefs = preferenceSet.iterator();
386:
387: while (prefs.hasNext()) {
388: PrefsPreference pref = (PrefsPreference) prefs
389: .next();
390: if (originalValues.containsKey(pref.getName())) {
391: pref.setValues((String[]) originalValues
392: .get(pref.getName()));
393: } else {
394: preferenceSet.remove(pref);
395: }
396: preferenceSet.flush();
397: }
398:
399: Iterator keys = originalValues.keySet().iterator();
400: while (keys.hasNext()) {
401: String key = (String) keys.next();
402: if (preferenceSet.get(key) == null) {
403: preferenceSet.add(key, Arrays
404: .asList((String[]) originalValues
405: .get(key)));
406: }
407: }
408: }
409: dirty = false;
410: backupValues(preferenceSet);
411: } catch (BackingStoreException e) {
412: String msg = "Preference backing store failed: "
413: + e.toString();
414: IOException ioe = new IOException(msg);
415: ioe.initCause(e);
416: throw ioe;
417: }
418:
419: }
420:
421: // internal methods used for debugging purposes only
422:
423: public String toString() {
424: return toString(0);
425: }
426:
427: public String toString(int indent) {
428: StringBuffer buffer = new StringBuffer(1000);
429: StringUtils.newLine(buffer, indent);
430: buffer.append(getClass().toString());
431: buffer.append(":");
432: StringUtils.newLine(buffer, indent);
433: buffer.append("{");
434: StringUtils.newLine(buffer, indent);
435: buffer.append("id='");
436: buffer.append(oid);
437: buffer.append("'");
438: StringUtils.newLine(buffer, indent);
439: buffer.append("definition-id='");
440: if (portletDefinition != null) {
441: buffer.append(portletDefinition.getId().toString());
442: } else {
443: buffer.append("null");
444: }
445: buffer.append("'");
446:
447: StringUtils.newLine(buffer, indent);
448: //buffer.append(((PreferenceSetImpl)preferences).toString(indent));
449:
450: StringUtils.newLine(buffer, indent);
451: buffer.append("}");
452: return buffer.toString();
453: }
454:
455: /**
456: * @see org.apache.pluto.om.entity.PortletEntity#getDescription(java.util.Locale)
457: */
458: public Description getDescription(Locale arg0) {
459: return portletDefinition.getDescription(arg0);
460: }
461:
462: /**
463: * <p>
464: * setPortletDefinition
465: * </p>
466: *
467: * @param composite
468: *
469: */
470: public void setPortletDefinition(PortletDefinition composite) {
471: if (composite != null) {
472: portletDefinition = (PortletDefinitionComposite) composite;
473: // if the portletDefinition is modified, clear threadlocal fragmentPortletDefinition cache
474: fragmentPortletDefinition.set(null);
475: this .appName = ((MutablePortletApplication) portletDefinition
476: .getPortletApplicationDefinition()).getName();
477: this .portletName = portletDefinition.getName();
478: } else {
479: throw new IllegalArgumentException(
480: "Cannot pass a null PortletDefinition to a PortletEntity.");
481: }
482: }
483:
484: /**
485: * @return Returns the principal.
486: */
487: public Principal getPrincipal() {
488: if (rcc == null) {
489: return new PortletEntityUserPrincipal(NO_PRINCIPAL);
490: }
491: RequestContext rc = rcc.getRequestContext();
492: Principal principal = rc.getUserPrincipal();
493: if (principal == null) {
494: principal = new PortletEntityUserPrincipal(NO_PRINCIPAL);
495: }
496: return principal;
497: }
498:
499: class PortletEntityUserPrincipal implements Principal {
500: String name;
501:
502: protected PortletEntityUserPrincipal(String name) {
503: this .name = name;
504: }
505:
506: /**
507: * <p>
508: * getName
509: * </p>
510: *
511: * @see java.security.Principal#getName()
512: * @return
513: */
514: public String getName() {
515: return name;
516: }
517:
518: /**
519: * <p>
520: * equals
521: * </p>
522: *
523: * @see java.lang.Object#equals(java.lang.Object)
524: * @param obj
525: * @return
526: */
527: public boolean equals(Object obj) {
528: if (obj != null && obj instanceof Principal) {
529: Principal p = (Principal) obj;
530: return name != null && p.getName() != null
531: && name.equals(p.getName());
532: } else {
533: return false;
534: }
535: }
536:
537: /**
538: * <p>
539: * hashCode
540: * </p>
541: *
542: * @see java.lang.Object#hashCode()
543: * @return
544: */
545: public int hashCode() {
546: if (name != null) {
547: return (getClass().getName() + ":" + name).hashCode();
548: } else {
549: return -1;
550: }
551: }
552:
553: /**
554: * <p>
555: * toString
556: * </p>
557: *
558: * @see java.lang.Object#toString()
559: * @return
560: */
561: public String toString() {
562: return name;
563: }
564: }
565:
566: /**
567: * <p>
568: * postRemoval
569: * </p>
570: *
571: * @see org.apache.jetspeed.components.persistence.store.RemovalAware#postRemoval(org.apache.jetspeed.components.persistence.store.PersistenceStore)
572: * @param store
573: * @throws {@link org.apache.jetspeed.persistence.store.PersistenceStoreRuntimeExcpetion}
574: * if the removal of the {@link java.util.prefs.Preference} related to this entity fails
575: */
576: public void postRemoval(PersistenceStore store) {
577:
578: }
579:
580: /**
581: * <p>
582: * preRemoval
583: * </p>
584: * not implemented.
585: *
586: * @see org.apache.jetspeed.components.persistence.store.RemovalAware#preRemoval(org.apache.jetspeed.components.persistence.store.PersistenceStore)
587: * @param store
588: */
589: public void preRemoval(PersistenceStore store) {
590: String rootForEntity = MutablePortletEntity.PORTLET_ENTITY_ROOT
591: + "/" + getId();
592: try {
593: if (Preferences.userRoot().nodeExists(rootForEntity)) {
594: Preferences.userRoot().node(rootForEntity).removeNode();
595: }
596: } catch (BackingStoreException e) {
597: throw new PersistenceStoreRuntimeExcpetion(e.toString(), e);
598: }
599:
600: }
601:
602: public String getPortletUniqueName() {
603: if (this .appName != null && this .portletName != null) {
604: return this .appName + "::" + this .portletName;
605: } else if (fragment != null) {
606: return fragment.getName();
607: } else {
608: return null;
609: }
610: }
611:
612: public void setFragment(Fragment fragment) {
613: this .fragment = fragment;
614: // if the fragment is set, clear threadlocal fragmentPortletDefinition cache
615: fragmentPortletDefinition.set(null);
616: }
617:
618: public int getRenderTimeoutCount() {
619: return timeoutCount;
620: }
621:
622: public synchronized void incrementRenderTimeoutCount() {
623: timeoutCount++;
624: }
625:
626: public synchronized void setExpiration(long expiration) {
627: this .expiration = expiration;
628: }
629:
630: public long getExpiration() {
631: return this .expiration;
632: }
633:
634: public void success() {
635: timeoutCount = 0;
636: }
637:
638: public void setRenderTimeoutCount(int timeoutCount) {
639: this .timeoutCount = timeoutCount;
640: }
641:
642: private boolean isEditDefaultsMode() {
643: boolean editDefaultsMode = false;
644:
645: PortletWindow curWindow = null;
646:
647: if (this .portletWindows != null) {
648: try {
649: curWindow = (PortletWindow) this .portletWindows
650: .iterator().next();
651: } catch (Exception e) {
652: }
653: }
654:
655: if (rcc != null) {
656: RequestContext context = rcc.getRequestContext();
657:
658: try {
659: PortletMode curMode = context.getPortalURL()
660: .getNavigationalState().getMode(curWindow);
661: editDefaultsMode = (JetspeedActions.EDIT_DEFAULTS_MODE
662: .equals(curMode));
663: } catch (Exception e) {
664: }
665: }
666:
667: return editDefaultsMode;
668: }
669: }
|