KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jahia > services > version > JahiaVersionDBService


1 //
2
// ____.
3
// __/\ ______| |__/\. _______
4
// __ .____| | \ | +----+ \
5
// _______| /--| | | - \ _ | : - \_________
6
// \\______: :---| : : | : | \________>
7
// |__\---\_____________:______: :____|____:_____\
8
// /_____|
9
//
10
// . . . i n j a h i a w e t r u s t . . .
11
//
12

13 package org.jahia.services.version;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Enumeration JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.ListIterator JavaDoc;
21 import java.util.Locale JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Vector JavaDoc;
24
25 import org.apache.log4j.Logger;
26 import org.jahia.exceptions.JahiaException;
27 import org.jahia.params.ParamBean;
28 import org.jahia.registries.ServicesRegistry;
29 import org.jahia.services.pages.JahiaPage;
30 import org.jahia.services.sites.JahiaSite;
31 import org.jahia.services.usermanager.JahiaUser;
32
33
34
35 /**
36  * DB implementation of the Versinoning service
37  * @author <a HREF="mailto:djilli@jahia.com">David Jilli</a>
38  */

39 public class JahiaVersionDBService extends JahiaVersionService
40 {
41     private static final String JavaDoc SHARED_LANGUAGE = "shared";
42     private static Logger logger = Logger.getLogger(JahiaVersionDBService.class);
43
44     private static final Locale JavaDoc SHARED_LANG_LOCALE = new Locale JavaDoc(SHARED_LANGUAGE, "");
45     private static JahiaVersionDBService mInstance;
46
47     //--------------------------------------------------------------------------
48
/**
49      * Default constructor.
50      *
51      * @exception JahiaException Raise a JahiaException when during initialization
52      * one of the needed services could not be instanciated.
53      */

54     protected JahiaVersionDBService () throws JahiaException {
55         logger.debug("***** Starting JahiaVersionDBService *****" );
56     }
57
58
59     //-------------------------------------------------------------------------
60
/**
61      * Create an new instance of the Version Service if the instance do not
62      * exist, or return the existing instance.
63      *
64      * @return Return the instance of the Version Service.
65      */

66     public static synchronized JahiaVersionDBService getInstance ()
67     {
68         if (mInstance == null)
69         {
70             try {
71                 mInstance = new JahiaVersionDBService ();
72             }
73             catch (JahiaException ex) {
74                 logger.debug("Could not create an instance of the JahiaVersionDBService class", ex);
75             }
76         }
77         return mInstance;
78     }
79
80     /**
81      * Is staging enabled for this site?
82      * @param int siteID, the site identifier
83      */

84     public boolean isStagingEnabled (int siteID) {
85
86         try
87         {
88             JahiaSite site = ServicesRegistry.getInstance().getJahiaSitesService().getSite(siteID);
89             if ( site==null){
90                 logger.debug("Requested site [" + siteID + "] is null...");
91                 return false;
92             }
93             return site.isStagingEnabled();
94         } catch (JahiaException je)
95         {
96             logger.debug("Couldn't get staging status...", je);
97             return false;
98         }
99     }
100
101
102     /**
103      * Is versioning enabled for this site?
104      * @param int siteID, the site identifier
105      */

106     public boolean isVersioningEnabled (int siteID) {
107         try {
108             JahiaSite site = ServicesRegistry.getInstance().getJahiaSitesService().getSite(siteID);
109             if ( site==null){
110                 logger.debug("Requested site [" + siteID + "] is null...");
111                 return false;
112             }
113             return site.isVersioningEnabled();
114
115        } catch (JahiaException je)
116        {
117             logger.debug("Couldn't get versioning status...", je);
118             return false;
119        }
120     }
121
122     /**
123      * @return the current versionID, which is the number of secondes since 1970
124      */

125     public int getCurrentVersionID()
126     {
127         java.util.Date JavaDoc d = new java.util.Date JavaDoc();
128         return (int)(d.getTime() / 1000);
129     }
130
131     /**
132      * @return the SaveVersion for a specified site
133      */

134     public JahiaSaveVersion getSiteSaveVersion(int siteID)
135     {
136         boolean staging = false;
137         boolean versioning = false;
138         // if (isStagingEnabled (siteID)) staging = true;
139
staging = true; /** @todo staging is always activated */
140         if (isVersioningEnabled (siteID)) versioning = true;
141
142         return new JahiaSaveVersion(staging, versioning);
143     }
144
145    /**
146     * Validate all the staged content from a page to which the user has WRITE+ADMIN access to
147     */

148     public void activateStagedPage ( int pageID, JahiaUser user, ParamBean jParams, StateModificationContext stateModifContext )
149         throws JahiaException
150     {
151         ServicesRegistry sr = ServicesRegistry.getInstance();
152
153         JahiaPage thePage = null;
154         thePage = sr.getJahiaPageService().lookupPage(pageID, jParams);
155         if (thePage != null)
156         {
157             int siteID = thePage.getJahiaID();
158             JahiaSaveVersion saveVersion = getSiteSaveVersion(siteID);
159             ActivationTestResults activationResults = thePage.activeStagingVersion(stateModifContext.getLanguageCodes(), saveVersion, user, jParams, stateModifContext);
160             logger.debug("activation results : " + activationResults.toString());
161         }
162     }
163
164
165     /**
166      * This method should be called if we have a vector of "Versionable"s that
167      * represent every version that the DB contains, and a ParamBean that
168      * contains the version we would like to load
169      * SHARED language has the highest priority!
170      * @param entryStateables a Vector of object implementing the EntryStateable interface
171      * @param jParams the ParamBean that contains the version we want to load
172      * @param ignoreLanguage if true, resolve entry state without checking specific language
173      * @return an element, or null if field doesn't exist in this version!
174      */

175     public EntryStateable resolveEntry( Vector JavaDoc entryStateables, EntryLoadRequest loadRequest , boolean ignoreLanguage)
176     {
177         /** @todo Multilanguage resolution implemented, must still do staging
178          * and active version checking... Also I'm not sure if we can access
179          * at this point the activeAndStagingVersionInfo table. Do we need to
180          * load the values first ?
181          */

182         EntryStateable result = null;
183
184         // we could probably optimize the code below by making sure it is called
185
// only once per request and by storing the requested version info
186
// once it has been resolved.
187
List JavaDoc clientLocales = loadRequest.getLocales();
188         if ( clientLocales.size() == 0 ){
189             // clientLocales.add(0, new Locale(SHARED_LANGUAGE, ""));
190
clientLocales.add(0, SHARED_LANG_LOCALE);
191         }
192         Locale JavaDoc sharedLocale = (Locale JavaDoc)clientLocales.get(0);
193         if ( !sharedLocale.toString().equals(SHARED_LANGUAGE) )
194         {
195             clientLocales.add(0, SHARED_LANG_LOCALE);
196         }
197         ListIterator JavaDoc clientLocalesIter = clientLocales.listIterator();
198
199         // we want to load the active version
200
if (loadRequest.isCurrent())
201         {
202             while (clientLocalesIter.hasNext() && result == null)
203             {
204                 Locale JavaDoc currentClientLocale = (Locale JavaDoc) clientLocalesIter.next();
205                 String JavaDoc langCodeToFind = currentClientLocale.toString();
206                 // now we must compare this value with the list of available
207
// languages to match them...
208
result = findActiveLanguageEntry(langCodeToFind, entryStateables, ignoreLanguage, loadRequest);
209                 if (result == null && !ignoreLanguage
210                   && currentClientLocale.getCountry().length() != 0) {
211                     // we have looked so far a country specific language, let's
212
// try now for the generic language.
213
langCodeToFind = currentClientLocale.getLanguage();
214                     result = findActiveLanguageEntry(langCodeToFind, entryStateables, ignoreLanguage, loadRequest);
215                 }
216             }
217         } else
218         // we want to load the staged version
219
if (loadRequest.getWorkflowState()>EntryLoadRequest.ACTIVE_WORKFLOW_STATE)
220         {
221             while (clientLocalesIter.hasNext() && (result==null))
222             {
223                 Locale JavaDoc currentClientLocale = (Locale JavaDoc) clientLocalesIter.next();
224                 String JavaDoc langCodeToFind = currentClientLocale.toString();
225                 // now we must compare this value with the list of available
226
// languages to match them...
227
result = findActiveOrStagingLanguageEntry(langCodeToFind, entryStateables, ignoreLanguage, loadRequest, false);
228                 if (result == null && !ignoreLanguage
229                   && currentClientLocale.getCountry().length() != 0) {
230                     // we have looked so far a country specific language, let's
231
// try now for the generic language.
232
langCodeToFind = currentClientLocale.getLanguage();
233                     result = findActiveOrStagingLanguageEntry(langCodeToFind, entryStateables, ignoreLanguage, loadRequest, false);
234                 }
235
236                 if ( result != null &&
237                      (result.getWorkflowState()==EntryLoadRequest.ACTIVE_WORKFLOW_STATE) ){
238                     loadRequest = new EntryLoadRequest(loadRequest);
239                     boolean withMarkedForDelete = loadRequest.isWithMarkedForDeletion();
240                     loadRequest.setWithMarkedForDeletion(true);
241                     // check that it's not marked for delete
242
EntryStateable stagedEntry =
243                         findActiveOrStagingLanguageEntry(langCodeToFind,
244                                                          entryStateables,
245                                                          ignoreLanguage,
246                                                          loadRequest, true);
247                     loadRequest.setWithMarkedForDeletion(withMarkedForDelete);
248                     if ( stagedEntry != null &&
249                          stagedEntry.getWorkflowState()>EntryLoadRequest.ACTIVE_WORKFLOW_STATE
250                          && stagedEntry.getVersionID() == EntryLoadRequest.DELETED_WORKFLOW_STATE ){
251                         result = null;
252                     }
253                 }
254             }
255         } else
256         // we want to load an old version
257
if (loadRequest.isVersioned())
258         {
259             int wantedVersionID = loadRequest.getVersionID();
260             // the following hashtable contains languages as keys, and the value is
261
// the Versionable that has the closest versionID to the wanted version ID
262
// for the specified language
263
Map JavaDoc lastVersion = new HashMap JavaDoc();
264             Map JavaDoc deletedVersion = new HashMap JavaDoc();
265             EntryStateable deletedVer = null;
266             EntryStateable storedVer = null;
267             for (int i=0; i<entryStateables.size(); i++)
268             {
269                 EntryStateable thisVer = (EntryStateable)entryStateables.elementAt(i);
270
271                 // remember deleted versions
272
if ( loadRequest.isWithDeleted() && thisVer.getWorkflowState()
273                      == EntryLoadRequest.VERSIONED_WORKFLOW_STATE && isDeleted(entryStateables,thisVer.getVersionID()) ){
274                     deletedVer = (EntryStateable)deletedVersion.get(thisVer.getLanguageCode());
275                     if ( deletedVer == null || ( (deletedVer.getLanguageCode().equals(thisVer.getLanguageCode()))
276                           && (thisVer.getVersionID()>deletedVer.getVersionID()))){
277                         deletedVersion.put(thisVer.getLanguageCode(), thisVer);
278                     }
279                 }
280
281                 if ((thisVer.getWorkflowState()<=1) && ( (thisVer.getVersionID()<=wantedVersionID || wantedVersionID==0) )
282                     || (thisVer.getWorkflowState() == 1 && loadRequest.getVersionID()==0) )
283                 {
284                     storedVer = (EntryStateable)lastVersion.get(thisVer.getLanguageCode());
285                     if ((storedVer==null) ||
286                           ( (storedVer.getLanguageCode().equals(thisVer.getLanguageCode()))
287                           && (thisVer.getVersionID()>storedVer.getVersionID())))
288                     {
289                         lastVersion.put(thisVer.getLanguageCode(), thisVer);
290                     }
291                 }
292             }
293
294             // should we use deleted version in place of archived version if they don't exist
295
if ( loadRequest.isWithDeleted() ){
296                 Iterator JavaDoc keys = deletedVersion.keySet().iterator();
297                 String JavaDoc lang = null;
298                 while (keys.hasNext()){
299                     lang = (String JavaDoc)keys.next();
300                     if (!lastVersion.containsKey(lang)){
301                         lastVersion.put(lang, deletedVersion.get(lang));
302                     }
303                 }
304             }
305
306             // ok now in lastVersion we have the closest versions for each language, we
307
// can do the resolving work easily now...
308
while (clientLocalesIter.hasNext() && (result==null))
309             {
310                 Locale JavaDoc currentClientLocale = (Locale JavaDoc) clientLocalesIter.next();
311                 String JavaDoc langCodeToFind = currentClientLocale.toString();
312                 EntryStateable thisVer = (EntryStateable)lastVersion.get(langCodeToFind);
313                 // we keep this version if not null & not deleted (status=-1 when deleted)
314
if ((thisVer!=null) && (thisVer.getWorkflowState()<=0)) {
315                     result=thisVer;
316                     break;
317                 }
318                 if (result == null && !ignoreLanguage
319                   && currentClientLocale.getCountry().length() != 0) {
320                     langCodeToFind = currentClientLocale.getLanguage();
321                     // we have looked so far a country specific language, let's
322
// try now for the generic language.
323
thisVer = (EntryStateable)lastVersion.get(langCodeToFind);
324                     // we keep this version if not null & not deleted (status=-1 when deleted)
325
if ((thisVer!=null) && (thisVer.getWorkflowState()<=0)) {
326                         result=thisVer;
327                         break;
328                     }
329                 }
330                 if ( !clientLocalesIter.hasNext()
331                      && result == null && ignoreLanguage
332                      && lastVersion.size()>0){
333                     // as we ignore the language, we return the first
334
result = (EntryStateable)lastVersion.values().iterator().next();
335                 }
336             }
337             if (result == null) {
338                 // we didn't find any versioned entry, let's use the active
339
// entry instead.
340
clientLocalesIter = clientLocales.listIterator();
341                 while (clientLocalesIter.hasNext() && (result==null))
342                 {
343                     Locale JavaDoc currentClientLocale = (Locale JavaDoc)clientLocalesIter.next();
344                     String JavaDoc langCodeToFind = currentClientLocale.toString();
345                     // now we must compare this value with the list of available
346
// languages to match them...
347
result = findActiveLanguageEntry(langCodeToFind,
348                       entryStateables, ignoreLanguage, loadRequest);
349                     if (result == null && !ignoreLanguage
350                       && currentClientLocale.getCountry().length() != 0) {
351                         // we have looked so far a country specific language, let's
352
// try now for the generic language.
353
langCodeToFind = currentClientLocale.getLanguage();
354                         result = findActiveLanguageEntry(langCodeToFind,
355                           entryStateables, ignoreLanguage, loadRequest);
356                     }
357                 }
358
359                 // now let's check the version ID of the active entry if it
360
// was found. We should use an active entry that is more
361
// recent that the version we are requesting...
362
if (result != null) {
363                     if (result.getVersionID() > loadRequest.getVersionID()) {
364                         // found version ID is bigger than the requested
365
// version, let's NOT return it.
366
result = null;
367                     }
368                 }
369             }
370
371         }
372
373         if (result == null) {
374             logger.debug("No entry found for entry load request " +
375                          loadRequest.toString() +
376                          " . Returning null !");
377         }
378
379         return result;
380     }
381
382     /**
383      * This method should be called if we have a vector of "Versionable"s that
384      * represent every version that the DB contains, and a ParamBean that
385      * contains the version we would like to load
386      * SHARED language has the highest priority!
387      * @param entryStateables a Vector of object implementing the EntryStateable interface
388      * @param jParams the ParamBean that contains the version we want to load
389      * @return an element, or null if field doesn't exist in this version!
390      */

391     public EntryStateable resolveEntry( Vector JavaDoc entryStateables, EntryLoadRequest loadRequest )
392     {
393         return resolveEntry(entryStateables,loadRequest,false);
394     }
395
396     private EntryStateable findActiveLanguageEntry(String JavaDoc langCodeToFind,
397             Vector JavaDoc entryStateables, boolean ignoreLanguage,
398             EntryLoadRequest loadRequest) {
399         EntryStateable result = null;
400
401         int availableLanguagesEnum = entryStateables.size();
402         Map JavaDoc deletedVersion = new HashMap JavaDoc();
403         EntryStateable deletedVer = null;
404
405         for (int i = availableLanguagesEnum - 1; i >= 0; i--) {
406             EntryStateable thisVer = (EntryStateable)entryStateables.get(i);
407             int thisVerWorkflowState = thisVer.getWorkflowState();
408             
409             int thisVerId = thisVer.getVersionID();
410             if (thisVerWorkflowState == 1 && (ignoreLanguage
411               || thisVer.getLanguageCode().equals(langCodeToFind))) {
412                 result = thisVer;
413                 break;
414             }
415             // remember deleted versions
416
if (loadRequest.isWithDeleted() && thisVerWorkflowState == -1
417               && isDeleted(entryStateables, thisVerId)) {
418               String JavaDoc thisVerLangCode = thisVer.getLanguageCode();
419               deletedVer = (EntryStateable)deletedVersion.get(thisVerLangCode);
420               if (deletedVer == null
421                 || deletedVer.getLanguageCode().equals(thisVerLangCode)
422                   && thisVerId > deletedVer.getVersionID()) {
423                   deletedVersion.put(thisVerLangCode, thisVer);
424               }
425             }
426         }
427
428         // should we use the deleted version if archive version don't exist
429
if (result == null && deletedVersion.size() > 0) {
430             result = (EntryStateable)deletedVersion.get(langCodeToFind);
431             if (result == null && ignoreLanguage) {
432                 result = (EntryStateable)deletedVersion.values().iterator().next();
433             }
434         }
435
436         return result;
437     }
438
439     /**
440      *
441      *
442      * @param langCodeToFind String
443      * @param entryStateables Vector
444      * @param ignoreLanguage boolean
445      * @param loadRequest EntryLoadRequest
446      * @param stagingOnly boolean
447      * @return EntryStateable
448      */

449     private EntryStateable findActiveOrStagingLanguageEntry(String JavaDoc langCodeToFind,
450         Vector JavaDoc entryStateables, boolean ignoreLanguage,
451         EntryLoadRequest loadRequest, boolean stagingOnly) {
452
453         Enumeration JavaDoc availableLanguagesEnum = entryStateables.elements();
454         EntryStateable activeVer = null;
455         EntryStateable stagedVer = null;
456         while ( (availableLanguagesEnum.hasMoreElements()) )
457         {
458             // @todo : should we need to handle withDeleted !
459

460             EntryStateable thisVer = (EntryStateable)availableLanguagesEnum.nextElement();
461             int thisVerWorkflowState = thisVer.getWorkflowState();
462             if (thisVerWorkflowState >= EntryLoadRequest.ACTIVE_WORKFLOW_STATE
463                 && (ignoreLanguage || thisVer.getLanguageCode().equals(langCodeToFind))
464                 || thisVerWorkflowState == EntryLoadRequest.DELETED_WORKFLOW_STATE
465                 && (ignoreLanguage || thisVer.getLanguageCode().equals(langCodeToFind))
466                 && loadRequest.isWithDeleted() && isDeleted(entryStateables, thisVer.getVersionID()))
467             {
468                 if (!stagingOnly && thisVerWorkflowState < EntryLoadRequest.STAGING_WORKFLOW_STATE){
469                     if ( activeVer == null ){
470                         activeVer = thisVer;
471                     } else if (activeVer.getVersionID()<thisVer.getVersionID()) {
472                         activeVer = thisVer;
473                     }
474                 } else if ( thisVerWorkflowState > EntryLoadRequest.ACTIVE_WORKFLOW_STATE ) {
475                     stagedVer = thisVer;
476                 }
477             }
478         }
479
480         if ( loadRequest.getWorkflowState()>EntryLoadRequest.ACTIVE_WORKFLOW_STATE ){
481             // we requested staging or active ( if staging doesn't exist )
482

483             if (stagedVer != null) {
484                 if (loadRequest.isWithMarkedForDeletion()) {
485                     return stagedVer;
486                 }
487                 else if (stagedVer.getVersionID()
488                          != EntryLoadRequest.DELETED_WORKFLOW_STATE) {
489                     return stagedVer;
490                 }
491             }
492         }
493
494         if ( activeVer != null ){
495             // we request active
496
if ( loadRequest.isWithDeleted() ){
497                 return activeVer;
498             } else if ( activeVer.getWorkflowState()
499                         != EntryLoadRequest.DELETED_WORKFLOW_STATE ){
500                 return activeVer;
501             }
502         }
503
504         return null;
505     }
506
507     /**
508      * Return true if the content object is deleted in all language at a given
509      * date.
510      * @param versionID
511      * @return
512      */

513     private boolean isDeleted(Vector JavaDoc entryStateables, int versionID){
514         ArrayList JavaDoc entryStates = getClosestVersionedEntryStates(entryStateables, versionID);
515         int size = entryStates.size();
516         EntryStateable entryState = null;
517         for ( int i=0 ; i<size; i++ ){
518             entryState = (EntryStateable)entryStates.get(i);
519             if ( entryState.getWorkflowState() !=
520                  ContentObjectEntryState.WORKFLOW_STATE_VERSIONING_DELETED ){
521                 return false;
522             }
523         }
524         return true;
525     }
526     /**
527      * Find and returns the closest versioned entry state for the given
528      * version ID, in ALL languages that weren't deleted at the time.
529      *
530      * @param versionID the identifier of the version we want to retrieve
531      * the list of languages that were actually active at the time (deleted
532      * languages are only taken into account if the version ID matches the
533      * entry state version ID).
534      *
535      * @return an ArrayList containing ContentObjectEntryState objects that
536      * are the various language entry states that correspond to the closest
537      * versions for each language.
538      * @return
539      * @throws JahiaException
540      */

541     private ArrayList JavaDoc getClosestVersionedEntryStates(Vector JavaDoc entryStateables, int versionID){
542
543         // let's test if we have a version id that exists prior to or equal
544
// to the one we've been asked for
545
Map JavaDoc closestInLanguage = new HashMap JavaDoc();
546         EntryStateable resultEntryState = null;
547         int size = entryStateables.size();
548         for ( int i=0 ; i<size ; i++ ){
549             EntryStateable curEntryState =
550                     (EntryStateable) entryStateables.get(i);
551             if ( ((curEntryState.getWorkflowState() == ContentObjectEntryState.WORKFLOW_STATE_ACTIVE) &&
552                   (curEntryState.getVersionID() <= versionID))
553                  ||
554                 ((curEntryState.getWorkflowState() == ContentObjectEntryState.WORKFLOW_STATE_VERSIONED) &&
555                   (curEntryState.getVersionID() <= versionID))
556                  ||
557                  ((curEntryState.getWorkflowState() == ContentObjectEntryState.WORKFLOW_STATE_VERSIONING_DELETED) &&
558                   (curEntryState.getVersionID() <= versionID))
559                  ) {
560
561                 // we found an acceptable versioned entry state, let's check
562
// if it's closer in the language.
563
// first we retrieve the current closest version ID for the
564
// language if it exists...
565
resultEntryState = (EntryStateable) closestInLanguage.get(curEntryState.getLanguageCode());
566                 if (resultEntryState != null) {
567                     // now let's test if it's closer to our previous result.
568
if (resultEntryState.getVersionID() <
569                         curEntryState.getVersionID()) {
570                         closestInLanguage.put(curEntryState.getLanguageCode(), curEntryState);
571                     }
572                 } else {
573                     // no version found for this language, let's add it.
574
closestInLanguage.put(curEntryState.getLanguageCode(), curEntryState);
575                 }
576             }
577         }
578         ArrayList JavaDoc resultEntryStates = new ArrayList JavaDoc(closestInLanguage.values());
579         return resultEntryStates;
580     }
581 }
582
Popular Tags