001/* 002 * Copyright 2025 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.repositoryapp.jcr; 017 018import java.util.Map; 019import java.util.Map.Entry; 020 021import javax.jcr.RepositoryException; 022import javax.jcr.Session; 023import javax.jcr.version.Version; 024import javax.jcr.version.VersionHistory; 025import javax.jcr.version.VersionIterator; 026 027import org.apache.jackrabbit.JcrConstants; 028import org.apache.jackrabbit.core.NodeImpl; 029import org.slf4j.Logger; 030 031/** 032 * Helper to manipulate the JCR version history 033 */ 034public final class VersionHistoryHelper 035{ 036 /** 037 * Result of a remove version history operation 038 */ 039 public enum RemoveHistoryResult 040 { 041 /** The version history has been removed */ 042 REMOVED, 043 /** The version history is still referenced and could not be removed */ 044 REFERENCED, 045 /** An error occurred preventing the removal */ 046 ERROR, 047 /** The version history was broken and has been removed */ 048 FIXED, 049 /** The version history is broken but could not be removed */ 050 UNFIXABLE 051 } 052 053 private VersionHistoryHelper() 054 { 055 // Utility class 056 } 057 058 /** 059 * Remove the version history if it is unused (ie the versionable node does not exist anymore) 060 * @param sessions the sessions used to determine if the versionable exist. Multiple session can be provided to search in multiple workspace for example. 061 * @param versionHistory the version history node to remove 062 * @param logger a logger to use to provide detailed information 063 * @return the result of the operation 064 * @throws RepositoryException if an error occurs 065 */ 066 public static RemoveHistoryResult removeUnusedHistory(Map<String, Session> sessions, VersionHistory versionHistory, Logger logger) throws RepositoryException 067 { 068 String versionableIdentifier = versionHistory.getVersionableIdentifier(); 069 070 // check that the history is actually unused 071 for (Entry<String, Session> entry: sessions.entrySet()) 072 { 073 try 074 { 075 entry.getValue().getNodeByIdentifier(versionableIdentifier); 076 return RemoveHistoryResult.REFERENCED; 077 } 078 catch (RepositoryException e) 079 { 080 // nothing 081 } 082 } 083 084 // unused node, we may remove associated version history 085 VersionIterator it = versionHistory.getAllVersions(); 086 087 int childNodesSize = 0; 088 089 while (it.hasNext()) 090 { 091 Version version = it.nextVersion(); 092 if (!JcrConstants.JCR_ROOTVERSION.equals(version.getName())) 093 { 094 childNodesSize++; 095 096 try 097 { 098 versionHistory.removeVersion(version.getName()); 099 } 100 catch (RepositoryException e) 101 { 102 logger.error("Error with version " + version.getName() + " of " + versionHistory.getIdentifier() + "... " + e); 103 return RemoveHistoryResult.ERROR; 104 } 105 } 106 } 107 108 if (childNodesSize == 0) 109 { 110 _removeEmptyHistory(versionHistory, sessions.get("default")); 111 try 112 { 113 sessions.get("default").getNodeByIdentifier(versionHistory.getIdentifier()); 114 115 logger.debug("Empty history node is " + versionHistory.getIdentifier()); 116 return RemoveHistoryResult.UNFIXABLE; 117 } 118 catch (RepositoryException e) 119 { 120 return RemoveHistoryResult.FIXED; 121 } 122 } 123 else 124 { 125 return RemoveHistoryResult.REMOVED; 126 } 127 } 128 129 private static void _removeEmptyHistory(VersionHistory vh, Session session) throws RepositoryException 130 { 131 var node = ((NodeImpl) session.getNode("/ametys:root")).addNodeWithUuid("historyrepair", JcrConstants.NT_UNSTRUCTURED, vh.getProperty(JcrConstants.JCR_VERSIONABLEUUID).getString()); 132 node.addMixin("mix:versionable"); 133 session.save(); 134 session.getWorkspace().getVersionManager().checkin(node.getPath()); 135 session.getWorkspace().getVersionManager().checkout(node.getPath()); 136 session.removeItem("/ametys:root/historyrepair"); 137 session.save(); 138 vh.removeVersion("1.0"); 139 } 140 141}