001: /*
002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/db/CmsPublishList.java,v $
003: * Date : $Date: 2008-02-27 12:05:43 $
004: * Version: $Revision: 1.29 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: */
031:
032: package org.opencms.db;
033:
034: import org.opencms.file.CmsObject;
035: import org.opencms.file.CmsProject;
036: import org.opencms.file.CmsResource;
037: import org.opencms.file.CmsResourceFilter;
038: import org.opencms.main.CmsException;
039: import org.opencms.main.CmsIllegalArgumentException;
040: import org.opencms.main.CmsLog;
041: import org.opencms.util.CmsFileUtil;
042: import org.opencms.util.CmsUUID;
043:
044: import java.io.Externalizable;
045: import java.io.IOException;
046: import java.io.ObjectInput;
047: import java.io.ObjectOutput;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.Collections;
051: import java.util.Iterator;
052: import java.util.List;
053:
054: import org.apache.commons.logging.Log;
055:
056: /**
057: * A container for all new/changed/deteled Cms resources that are published together.<p>
058: *
059: * Only classes inside the org.opencms.db package can add or remove elements to or from this list.
060: * This allows the OpenCms API to pass the list around between classes, but with restricted access to
061: * create this list.<p>
062: *
063: * To create a publish list, one of the public constructors must be used in order to set the basic operation mode
064: * (project publish or direct publish).
065: * After this, use <code>{@link org.opencms.db.CmsDriverManager#fillPublishList(CmsDbContext, CmsPublishList)}</code>
066: * to fill the actual values of the publish list.<p>
067: *
068: * @author Alexander Kandzior
069: * @author Thomas Weckert
070: *
071: * @version $Revision: 1.29 $
072: *
073: * @since 6.0.0
074: *
075: * @see org.opencms.db.CmsDriverManager#fillPublishList(CmsDbContext, CmsPublishList)
076: */
077: public class CmsPublishList implements Externalizable {
078:
079: /** The log object for this class. */
080: private static final Log LOG = CmsLog.getLog(CmsPublishList.class);
081:
082: /** Indicates a non existent object in the serialized data. */
083: private static final int NIL = -1;
084:
085: /** Serial version UID required for safe serialization. */
086: private static final long serialVersionUID = -2578909250462750927L;
087:
088: /** Length of a serialized uuid. */
089: private static final int UUID_LENGTH = CmsUUID.getNullUUID()
090: .toByteArray().length;
091:
092: /** The list of deleted Cms folder resources to be published.<p> */
093: private List m_deletedFolderList;
094:
095: /** The list of direct publish resources. */
096: private List m_directPublishResources;
097:
098: /** The list of new/changed/deleted Cms file resources to be published.<p> */
099: private List m_fileList;
100:
101: /** The list of new/changed Cms folder resources to be published.<p> */
102: private List m_folderList;
103:
104: /** Flag to indicate if the list needs to be revived. */
105: private boolean m_needsRevive = false;
106:
107: /** The id of the project that is to be published. */
108: private CmsUUID m_projectId;
109:
110: /** The publish history ID.<p> */
111: private CmsUUID m_publishHistoryId;
112:
113: /** Indicates if siblings of the resources in the list should also be published. */
114: private boolean m_publishSiblings;
115:
116: /** Indicates if sub-resources in folders should be published (for direct publish only). */
117: private boolean m_publishSubResources;
118:
119: /**
120: * Empty constructor.<p>
121: */
122: public CmsPublishList() {
123:
124: // noop
125: }
126:
127: /**
128: * Constructs a publish list for a given project.<p>
129: *
130: * @param project the project to publish, this should always be the id of the current project
131: */
132: public CmsPublishList(CmsProject project) {
133:
134: this (project, null, false, true);
135: }
136:
137: /**
138: * Constructs a publish list for a single direct publish resource.<p>
139: *
140: * @param directPublishResource a VFS resource to be published directly
141: * @param publishSiblings indicates if all siblings of the selected resources should be published
142: */
143: public CmsPublishList(CmsResource directPublishResource,
144: boolean publishSiblings) {
145:
146: this (null, Collections.singletonList(directPublishResource),
147: publishSiblings, true);
148: }
149:
150: /**
151: * Constructs a publish list for a list of direct publish resources.<p>
152: *
153: * @param directPublishResources a list of <code>{@link CmsResource}</code> instances to be published directly
154: * @param publishSiblings indicates if all siblings of the selected resources should be published
155: */
156: public CmsPublishList(List directPublishResources,
157: boolean publishSiblings) {
158:
159: this (null, directPublishResources, publishSiblings, true);
160: }
161:
162: /**
163: * Constructs a publish list for a list of direct publish resources.<p>
164: *
165: * @param directPublishResources a list of <code>{@link CmsResource}</code> instances to be published directly
166: * @param publishSiblings indicates if all siblings of the selected resources should be published
167: * @param publishSubResources indicates if sub-resources in folders should be published (for direct publish only)
168: */
169: public CmsPublishList(List directPublishResources,
170: boolean publishSiblings, boolean publishSubResources) {
171:
172: this (null, directPublishResources, publishSiblings,
173: publishSubResources);
174: }
175:
176: /**
177: * Internal constructor for a publish list.<p>
178: *
179: * @param project the project to publish
180: * @param directPublishResources the list of direct publish resources
181: * @param publishSiblings indicates if all siblings of the selected resources should be published
182: * @param publishSubResources indicates if sub-resources in folders should be published (for direct publish only)
183: */
184: private CmsPublishList(CmsProject project,
185: List directPublishResources, boolean publishSiblings,
186: boolean publishSubResources) {
187:
188: m_fileList = new ArrayList();
189: m_folderList = new ArrayList();
190: m_deletedFolderList = new ArrayList();
191: m_publishHistoryId = new CmsUUID();
192: m_publishSiblings = publishSiblings;
193: m_publishSubResources = publishSubResources;
194: m_projectId = (project != null) ? project.getUuid() : null;
195: if (directPublishResources != null) {
196: // reduce list of folders to minimum
197: m_directPublishResources = Collections
198: .unmodifiableList(CmsFileUtil
199: .removeRedundantResources(directPublishResources));
200: }
201: }
202:
203: /**
204: * Returns a list of all resources in the publish list,
205: * including folders and files.<p>
206: *
207: * @return a list of {@link CmsResource} objects
208: */
209: public List getAllResources() {
210:
211: List all = new ArrayList();
212: all.addAll(m_folderList);
213: all.addAll(m_fileList);
214: all.addAll(m_deletedFolderList);
215:
216: Collections.sort(all, CmsResource.COMPARE_ROOT_PATH);
217: return Collections.unmodifiableList(all);
218: }
219:
220: /**
221: * Returns a list of folder resources with the given state.<p>
222: *
223: * @return a list of folder resources with the desired state
224: */
225: public List getDeletedFolderList() {
226:
227: if (m_needsRevive) {
228: return null;
229: } else {
230: return m_deletedFolderList;
231: }
232: }
233:
234: /**
235: * Returns the list of resources that should be published for a "direct" publish operation.<p>
236: *
237: * Will return <code>null</code> if this publish list was not initilaized for a "direct publish" but
238: * for a project publish.<p>
239: *
240: * @return the list of resources that should be published for a "direct" publish operation, or <code>null</code>
241: */
242: public List getDirectPublishResources() {
243:
244: if (m_needsRevive) {
245: return null;
246: } else {
247: return m_directPublishResources;
248: }
249: }
250:
251: /**
252: * Returns an unmodifiable list of the Cms file resources in this publish list.<p>
253: *
254: * @return the list with the Cms file resources in this publish list
255: */
256: public List getFileList() {
257:
258: if (m_needsRevive) {
259: return null;
260: } else {
261: return Collections.unmodifiableList(m_fileList);
262: }
263: }
264:
265: /**
266: * Returns an unmodifiable list of the new/changed Cms folder resources in this publish list.<p>
267: *
268: * @return the list with the new/changed Cms file resources in this publish list
269: */
270: public List getFolderList() {
271:
272: if (m_needsRevive) {
273: return null;
274: } else {
275: return Collections.unmodifiableList(m_folderList);
276: }
277: }
278:
279: /**
280: * Returns the id of the project that should be published, or <code>-1</code> if this publish list
281: * is initialized for a "direct publish" operation.<p>
282: *
283: * @return the id of the project that should be published, or <code>-1</code>
284: */
285: public CmsUUID getProjectId() {
286:
287: return m_projectId;
288: }
289:
290: /**
291: * Returns the publish history Id for this publish list.<p>
292: *
293: * @return the publish history Id
294: */
295: public CmsUUID getPublishHistoryId() {
296:
297: return m_publishHistoryId;
298: }
299:
300: /**
301: * Checks if this is a publish list is used for a "direct publish" operation.<p>
302: *
303: * @return true if this is a publish list is used for a "direct publish" operation
304: */
305: public boolean isDirectPublish() {
306:
307: return (m_projectId == null);
308: }
309:
310: /**
311: * Returns <code>true</code> if all siblings of the project resources are to be published.<p>
312: *
313: * @return <code>true</code> if all siblings of the project resources are to be publisheds
314: */
315: public boolean isPublishSiblings() {
316:
317: return m_publishSiblings;
318: }
319:
320: /**
321: * Returns <code>true</code> if sub-resources in folders should be published (for direct publish only).<p>
322: *
323: * @return <code>true</code> if sub-resources in folders should be published (for direct publish only)
324: */
325: public boolean isPublishSubResources() {
326:
327: return m_publishSubResources;
328: }
329:
330: /**
331: * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
332: */
333: public void readExternal(ObjectInput in) throws IOException {
334:
335: // read the history id
336: m_publishHistoryId = internalReadUUID(in);
337: // read the project id
338: m_projectId = internalReadUUID(in);
339: if (m_projectId.isNullUUID()) {
340: m_projectId = null;
341: }
342: // read the flags
343: m_publishSiblings = (in.readInt() != 0);
344: m_publishSubResources = (in.readInt() != 0);
345: // read the list of direct published resources
346: m_directPublishResources = internalReadUUIDList(in);
347: // read the list of published files
348: m_fileList = internalReadUUIDList(in);
349: // read the list of published folders
350: m_folderList = internalReadUUIDList(in);
351: // read the list of deleted folders
352: m_deletedFolderList = internalReadUUIDList(in);
353: // set revive flag to indicate that resource lists must be revived
354: m_needsRevive = true;
355: }
356:
357: /**
358: * Revives the publish list by populating the internal resource lists with <code>CmsResource</code> instances.<p>
359: *
360: * @param cms a cms object used to read the resource instances
361: */
362: public void revive(CmsObject cms) {
363:
364: if (m_needsRevive) {
365: if (m_directPublishResources != null) {
366: m_directPublishResources = internalReadResourceList(
367: cms, m_directPublishResources);
368: }
369: if (m_fileList != null) {
370: m_fileList = internalReadResourceList(cms, m_fileList);
371: }
372: if (m_folderList != null) {
373: m_folderList = internalReadResourceList(cms,
374: m_folderList);
375: }
376: if (m_deletedFolderList != null) {
377: m_deletedFolderList = internalReadResourceList(cms,
378: m_deletedFolderList);
379: }
380: m_needsRevive = false;
381: }
382: }
383:
384: /**
385: * Returns the number of all resources to be published.<p>
386: *
387: * @return the number of all resources to be published
388: */
389: public int size() {
390:
391: if (m_needsRevive) {
392: return 0;
393: } else {
394: return m_folderList.size() + m_fileList.size()
395: + m_deletedFolderList.size();
396: }
397: }
398:
399: /**
400: * @see java.lang.Object#toString()
401: */
402: public String toString() {
403:
404: StringBuffer result = new StringBuffer();
405: result.append("\n[\n");
406: if (isDirectPublish()) {
407: result.append("direct publish of resources: ").append(
408: m_directPublishResources.toString()).append("\n");
409: } else {
410: result.append("publish of project: ").append(m_projectId)
411: .append("\n");
412: }
413: result.append("publish history ID: ").append(
414: m_publishHistoryId.toString()).append("\n");
415: result.append("resources: ").append(m_fileList.toString())
416: .append("\n");
417: result.append("folders: ").append(m_folderList.toString())
418: .append("\n");
419: result.append("deletedFolders: ").append(
420: m_deletedFolderList.toString()).append("\n");
421: result.append("]\n");
422: return result.toString();
423: }
424:
425: /**
426: * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
427: */
428: public void writeExternal(ObjectOutput out) throws IOException {
429:
430: // write the history id
431: out.write(m_publishHistoryId.toByteArray());
432: // write the project id
433: out.write((m_projectId != null) ? m_projectId.toByteArray()
434: : CmsUUID.getNullUUID().toByteArray());
435: // write the flags
436: out.writeInt((m_publishSiblings) ? 1 : 0);
437: out.writeInt((m_publishSubResources) ? 1 : 0);
438: // write the list of direct publish resources by writing the uuid of each resource
439: if (m_directPublishResources != null) {
440: out.writeInt(m_directPublishResources.size());
441: for (Iterator i = m_directPublishResources.iterator(); i
442: .hasNext();) {
443: out.write(((CmsResource) i.next()).getStructureId()
444: .toByteArray());
445: }
446: } else {
447: out.writeInt(NIL);
448: }
449: // write the list of published files by writing the uuid of each resource
450: if (m_fileList != null) {
451: out.writeInt(m_fileList.size());
452: for (Iterator i = m_fileList.iterator(); i.hasNext();) {
453: out.write(((CmsResource) i.next()).getStructureId()
454: .toByteArray());
455: }
456: } else {
457: out.writeInt(NIL);
458: }
459: // write the list of published folders by writing the uuid of each resource
460: if (m_folderList != null) {
461: out.writeInt(m_folderList.size());
462: for (Iterator i = m_folderList.iterator(); i.hasNext();) {
463: out.write(((CmsResource) i.next()).getStructureId()
464: .toByteArray());
465: }
466: } else {
467: out.writeInt(NIL);
468: }
469: // write the list of deleted folders by writing the uuid of each resource
470: if (m_deletedFolderList != null) {
471: out.writeInt(m_deletedFolderList.size());
472: for (Iterator i = m_deletedFolderList.iterator(); i
473: .hasNext();) {
474: out.write(((CmsResource) i.next()).getStructureId()
475: .toByteArray());
476: }
477: } else {
478: out.writeInt(NIL);
479: }
480: }
481:
482: /**
483: * Adds a new/changed Cms folder resource to the publish list.<p>
484: *
485: * @param resource a new/changed Cms folder resource
486: * @param check if set an exception is thrown if the specified resource is unchanged,
487: * if not set the resource is ignored
488: *
489: * @throws IllegalArgumentException if the specified resource is unchanged
490: */
491: protected void add(CmsResource resource, boolean check)
492: throws IllegalArgumentException {
493:
494: if (check) {
495: // it is essential that this method is only visible within the db package!
496: if (resource.getState().isUnchanged()) {
497: throw new CmsIllegalArgumentException(
498: Messages
499: .get()
500: .container(
501: Messages.ERR_PUBLISH_UNCHANGED_RESOURCE_1,
502: resource.getRootPath()));
503: }
504: }
505: if (resource.isFolder()) {
506: if (resource.getState().isDeleted()) {
507: if (!m_deletedFolderList.contains(resource)) {
508: // only add files not already contained in the list
509: m_deletedFolderList.add(resource);
510: }
511: } else {
512: if (!m_folderList.contains(resource)) {
513: // only add files not already contained in the list
514: m_folderList.add(resource);
515: }
516: }
517: } else {
518: if (!m_fileList.contains(resource)) {
519: // only add files not already contained in the list
520: // this is required to make sure no siblings are duplicated
521: m_fileList.add(resource);
522: }
523: }
524: }
525:
526: /**
527: * Appends all the given resources to this publish list.<p>
528: *
529: * @param resources resources to be added to this publish list
530: * @param check if set an exception is thrown if the a resource is unchanged,
531: * if not set the resource is ignored
532: *
533: * @throws IllegalArgumentException if one of the resources is unchanged
534: */
535: protected void addAll(Collection resources, boolean check)
536: throws IllegalArgumentException {
537:
538: // it is essential that this method is only visible within the db package!
539: Iterator i = resources.iterator();
540: while (i.hasNext()) {
541: add((CmsResource) i.next(), check);
542: }
543: }
544:
545: /**
546: * @see java.lang.Object#finalize()
547: */
548: protected void finalize() throws Throwable {
549:
550: try {
551: if (m_fileList != null) {
552: m_fileList.clear();
553: }
554: if (m_folderList != null) {
555: m_folderList.clear();
556: }
557: if (m_deletedFolderList != null) {
558: m_deletedFolderList.clear();
559: }
560: } catch (Throwable t) {
561: // ignore
562: }
563:
564: super .finalize();
565: }
566:
567: /**
568: * Initializes the publish list, ensuring all internal lists are in the right order.<p>
569: */
570: protected void initialize() {
571:
572: if (m_folderList != null) {
573: // ensure folders are sorted starting with parent folders
574: Collections.sort(m_folderList,
575: CmsResource.COMPARE_ROOT_PATH);
576: }
577:
578: if (m_fileList != null) {
579: // ensure files are sorted starting with files in parent folders
580: Collections.sort(m_fileList, CmsResource.COMPARE_ROOT_PATH);
581: }
582:
583: if (m_deletedFolderList != null) {
584: // ensure deleted folders are sorted starting with child folders
585: Collections.sort(m_deletedFolderList,
586: CmsResource.COMPARE_ROOT_PATH);
587: Collections.reverse(m_deletedFolderList);
588: }
589: }
590:
591: /**
592: * Removes a Cms resource from the publish list.<p>
593: *
594: * @param resource a Cms resource
595: *
596: * @return true if this publish list contains the specified resource
597: *
598: * @see List#remove(java.lang.Object)
599: */
600: protected boolean remove(CmsResource resource) {
601:
602: // it is essential that this method is only visible within the db package!
603: boolean ret = m_fileList.remove(resource);
604: ret |= m_folderList.remove(resource);
605: ret |= m_deletedFolderList.remove(resource);
606: return ret;
607: }
608:
609: /**
610: * Builds a list of <code>CmsResource</code> instances from a list of resource structure ids.<p>
611: *
612: * @param cms a cms object
613: * @param uuidList the list of structure ids
614: * @return a list of <code>CmsResource</code> instances
615: */
616: private List internalReadResourceList(CmsObject cms, List uuidList) {
617:
618: List resList = new ArrayList(uuidList.size());
619: for (Iterator i = uuidList.iterator(); i.hasNext();) {
620: try {
621: CmsResource res = cms.readResource((CmsUUID) i.next(),
622: CmsResourceFilter.ALL);
623: resList.add(res);
624: } catch (CmsException exc) {
625: LOG.error(exc);
626: }
627: }
628:
629: return resList;
630: }
631:
632: /**
633: * Reads a UUID from an object input.<p>
634: *
635: * @param in the object input
636: * @return a UUID
637: * @throws IOException
638: */
639: private CmsUUID internalReadUUID(ObjectInput in) throws IOException {
640:
641: byte[] bytes = new byte[UUID_LENGTH];
642: in.readFully(bytes, 0, UUID_LENGTH);
643: return new CmsUUID(bytes);
644: }
645:
646: /**
647: * Reads a sequence of UUIDs from an objetc input and builds a list of <code>CmsResource</code> instances from it.<p>
648: *
649: * @param in the object input
650: * @return a list of <code>{@link CmsResource}</code> instances
651: *
652: * @throws IOException if something goes wrong
653: */
654: private List internalReadUUIDList(ObjectInput in)
655: throws IOException {
656:
657: List result = null;
658:
659: int i = in.readInt();
660: if (i >= 0) {
661: result = new ArrayList();
662: while (i > 0) {
663: result.add(internalReadUUID(in));
664: i--;
665: }
666: }
667:
668: return result;
669: }
670: }
|