001/* 002 * Copyright 2016 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.workflow.store; 017 018import javax.jcr.Node; 019import javax.jcr.Repository; 020import javax.jcr.RepositoryException; 021import javax.jcr.Session; 022 023import org.ametys.plugins.repository.version.VersionAwareAmetysObject; 024import org.ametys.plugins.workflow.repository.WorkflowAwareAmetysObject; 025 026import com.opensymphony.workflow.StoreException; 027import com.opensymphony.workflow.spi.WorkflowEntry; 028 029/** 030 * Workflow store that creates local entries for an Ametys object. 031 * Each entry belong to an ametys object as soon as the entry is created. 032 */ 033public class AmetysObjectWorkflowStore extends AbstractJackrabbitWorkflowStore 034{ 035 /** The ametys object */ 036 protected WorkflowAwareAmetysObject _ametysObject; 037 038 /** Temporary entry created in memory */ 039 protected WorkflowEntry _inMemoryEntry; 040 041 042 private boolean _preserveHistory; 043 044 /** 045 * Creates a workflow store for Ametys object. 046 * The history steps will be clear on workflow completion. 047 * @param repository the JCR Repository to use. 048 * @param ametysObject The ametys object for this store. Can be null in case of an object creation. 049 */ 050 public AmetysObjectWorkflowStore(Repository repository, WorkflowAwareAmetysObject ametysObject) 051 { 052 this(repository, ametysObject, false); 053 } 054 055 /** 056 * Creates a workflow store for Ametys object. 057 * @param repository the JCR Repository to use. 058 * @param ametysObject The ametys object for this store. Can be null in case of an object creation. 059 * @param preserveHistory Set to true to preserve history steps when workflow is complete. 060 */ 061 public AmetysObjectWorkflowStore(Repository repository, WorkflowAwareAmetysObject ametysObject, boolean preserveHistory) 062 { 063 super(repository); 064 _ametysObject = _ensureBaseVersion(ametysObject); 065 _preserveHistory = preserveHistory; 066 } 067 068 @Override 069 public boolean shouldClearHistory() 070 { 071 return !_preserveHistory; 072 } 073 074 /** 075 * Ametys object getter 076 * @return The ametys object 077 */ 078 public WorkflowAwareAmetysObject getAmetysObject() 079 { 080 return _ametysObject; 081 } 082 083 @Override 084 protected Session _getSession() throws RepositoryException 085 { 086 return _ametysObject != null ? _ametysObject.getNode().getSession() : null; 087 } 088 089 @Override 090 protected void _release(Session session) 091 { 092 // do nothing since the store use the ametys object session 093 } 094 095 /** 096 * Provides the ametys object node in the current session 097 * @return node 098 * @throws RepositoryException on repository error 099 */ 100 protected Node _getAmetysObjectNode() throws RepositoryException 101 { 102 return _ametysObject.getNode(); 103 } 104 105 @Override 106 protected void _createRootNode() throws RepositoryException 107 { 108 if (_ametysObject == null) 109 { 110 if (_log.isDebugEnabled()) 111 { 112 _log.debug("Ametys object does not currently exist. Root node creation will be delayed. The creation will be done once the ametys object is available."); 113 } 114 115 return; 116 } 117 118 Session session = null; 119 try 120 { 121 session = _getSession(); 122 123 Node contentNode = _getAmetysObjectNode(); 124 125 if (!contentNode.hasNode("ametys-internal:workflows")) 126 { 127 if (_log.isDebugEnabled()) 128 { 129 _log.debug(String.format("Creating osworkflow root node for the ametys object '%s'.", _ametysObject.getId())); 130 } 131 132 contentNode.addNode("ametys-internal:workflows"); 133 } 134 else 135 { 136 if (_log.isDebugEnabled()) 137 { 138 _log.debug(String.format("Existing osworkflow root node of the ametys object '%s', skipping creation.", _ametysObject.getId())); 139 } 140 } 141 } 142 finally 143 { 144 _release(session); 145 } 146 } 147 148 @Override 149 protected Node _getRootNode(Session session) throws RepositoryException 150 { 151 Node contentNode = _getAmetysObjectNode(); 152 return contentNode.getNode("ametys-internal:workflows"); 153 } 154 155 @Override 156 protected Node _getOrCreateParentEntryNode(Node root, long id) throws RepositoryException 157 { 158 return root; 159 } 160 161 @Override 162 protected synchronized long _getNextEntryId() throws RepositoryException 163 { 164 if (_ametysObject == null) 165 { 166 // default workflow entry id 167 return 0; 168 } 169 170 return super._getNextEntryId(); 171 } 172 173 /** 174 * Store a new workflow entry into the JCR repository 175 * @param workflowEntry The new entry 176 * @throws StoreException on error 177 */ 178 @Override 179 protected void storeNewEntry(WorkflowEntry workflowEntry) throws StoreException 180 { 181 // Temporary store in memory if ametys object does not exist currently. 182 // Otherwise persist the entry into the JCR repository 183 if (_ametysObject == null) 184 { 185 _inMemoryEntry = workflowEntry; 186 return; 187 } 188 189 super.storeNewEntry(workflowEntry); 190 } 191 192 /** 193 * Bind an ametys object to this store. Must be done in case of an object 194 * creation. As soon as the ametys object is created, the store must be 195 * notified to persist the workflow entry into the repository. 196 * @param ametysObject The ametys object to bind 197 * @throws StoreException on error 198 */ 199 public void bindAmetysObject(WorkflowAwareAmetysObject ametysObject) throws StoreException 200 { 201 _ametysObject = _ensureBaseVersion(ametysObject); 202 203 if (_inMemoryEntry != null) 204 { 205 // Create root node 206 try 207 { 208 _createRootNode(); 209 } 210 catch (RepositoryException e) 211 { 212 throw new StoreException("Unable to create the root node", e); 213 } 214 215 // Persist the memory entry into the repository 216 storeNewEntry(_inMemoryEntry); 217 _inMemoryEntry = null; 218 } 219 } 220 221 private WorkflowAwareAmetysObject _ensureBaseVersion(WorkflowAwareAmetysObject ametysObject) 222 { 223 if (ametysObject instanceof VersionAwareAmetysObject) 224 { 225 VersionAwareAmetysObject vao = (VersionAwareAmetysObject) ametysObject; 226 227 if (vao.getRevision() != null) 228 { 229 // switch to base 230 vao.switchToRevision(null); 231 } 232 233 } 234 235 return ametysObject; 236 } 237 238}