001/* 002 * Copyright 2017 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.odfweb.observation; 017 018import java.util.Collections; 019import java.util.Map; 020import java.util.Set; 021 022import org.apache.avalon.framework.context.ContextException; 023import org.apache.avalon.framework.context.Contextualizable; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.cocoon.components.ContextHelper; 028import org.apache.cocoon.environment.Context; 029import org.apache.cocoon.environment.Request; 030 031import org.ametys.cms.ObservationConstants; 032import org.ametys.cms.repository.Content; 033import org.ametys.core.observation.Event; 034import org.ametys.core.observation.Observer; 035import org.ametys.odf.ODFHelper; 036import org.ametys.odf.ProgramItem; 037import org.ametys.odf.course.Course; 038import org.ametys.odf.program.Program; 039import org.ametys.odf.program.SubProgram; 040import org.ametys.plugins.odfweb.repository.FirstLevelPageFactory; 041import org.ametys.plugins.repository.AmetysObjectIterable; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.RepositoryConstants; 044import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 045import org.ametys.plugins.repository.query.expression.Expression; 046import org.ametys.plugins.repository.query.expression.VirtualFactoryExpression; 047import org.ametys.runtime.plugin.component.AbstractLogEnabled; 048import org.ametys.web.repository.page.Page; 049import org.ametys.web.repository.page.PageQueryHelper; 050/** 051 * Abstract {@link Observer} for observing events on a Program or Course. 052 */ 053public abstract class AbstractODFObserver extends AbstractLogEnabled implements Observer, Serviceable, Contextualizable 054{ 055 /** The context. */ 056 protected org.apache.avalon.framework.context.Context _context; 057 /** Cocoon context. */ 058 protected Context _cocoonContext; 059 /** Ametys object resolver. */ 060 protected AmetysObjectResolver _resolver; 061 /** The ODF helper */ 062 protected ODFHelper _odfHelper; 063 064 @Override 065 public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException 066 { 067 _context = context; 068 _cocoonContext = (Context) context.get(org.apache.cocoon.Constants.CONTEXT_ENVIRONMENT_CONTEXT); 069 } 070 071 @Override 072 public void service(ServiceManager manager) throws ServiceException 073 { 074 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 075 _odfHelper = (ODFHelper) manager.lookup(ODFHelper.ROLE); 076 } 077 078 /** 079 * The workspace to use. Default to {@link RepositoryConstants#DEFAULT_WORKSPACE default}, 080 * override this method to work on a different workspace 081 * @return The workspace to use 082 */ 083 protected String _workspaceToUse() 084 { 085 return RepositoryConstants.DEFAULT_WORKSPACE; 086 } 087 088 @Override 089 public void observe(Event event, Map<String, Object> transientVars) throws Exception 090 { 091 Request request = ContextHelper.getRequest(_context); 092 093 // Retrieve current workspace 094 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 095 096 try 097 { 098 // Switch to required workspace 099 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, _workspaceToUse()); 100 101 AmetysObjectIterable<Page> rootPages = _getODFRootPages(); 102 if (rootPages.getSize() == 0) 103 { 104 getLogger().debug("There is no ODF root page, nothing to invalidate"); 105 return; 106 } 107 108 Set<Program> programs = _getPrograms(event); 109 if (programs.size() == 0) 110 { 111 getLogger().debug("There is no concerned programs"); 112 return; 113 } 114 115 for (Page odfRootPage : rootPages) 116 { 117 _internalObserve(event, transientVars, odfRootPage, programs, _getSubProgram(event), _getCourse(event)); 118 } 119 } 120 catch (Exception e) 121 { 122 getLogger().error("Unable to observe event: " + event, e); 123 } 124 finally 125 { 126 // Restore current workspace 127 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 128 } 129 } 130 131 /** 132 * Do the actual work. 133 * @param event the event. 134 * @param transientVars transientVars passed from one Observer to another when processing a single Event. 135 * This may allow optimizations between observers. 136 * @param odfRootPage the Page holding the virtual factory. 137 * @param rootPrograms the root programs. 138 * @param subProgram The subprogram. Can be null 139 * @param course The course. Can be null. 140 * @throws Exception if an error occured 141 */ 142 protected abstract void _internalObserve(Event event, Map<String, Object> transientVars, Page odfRootPage, Set<Program> rootPrograms, SubProgram subProgram, Course course) throws Exception; 143 144 /** 145 * Get the ODF root pages 146 * @return the ODF root pages 147 */ 148 protected AmetysObjectIterable<Page> _getODFRootPages() 149 { 150 Expression expression = new VirtualFactoryExpression(FirstLevelPageFactory.class.getName()); 151 String query = PageQueryHelper.getPageXPathQuery(null, null, null, expression, null); 152 153 return _resolver.query(query); 154 } 155 156 /** 157 * Retrieve the target of the observer 158 * @param event The event 159 * @return The target 160 * @throws Exception if failed to get content 161 */ 162 protected Content _getContentArgument(Event event) throws Exception 163 { 164 return (Content) event.getArguments().get(ObservationConstants.ARGS_CONTENT); 165 } 166 167 /** 168 * Get the target abstract program 169 * @param event the event 170 * @return the target abstract program or <code>null</code> 171 * @throws Exception if failed to get content 172 */ 173 protected SubProgram _getSubProgram(Event event) throws Exception 174 { 175 Content content = _getContentArgument(event); 176 177 if (content instanceof SubProgram) 178 { 179 return (SubProgram) content; 180 } 181 return null; 182 } 183 184 /** 185 * Get the target course 186 * @param event the event 187 * @return the target course or <code>null</code> 188 * @throws Exception if failed to get content 189 */ 190 protected Course _getCourse(Event event) throws Exception 191 { 192 Content content = _getContentArgument(event); 193 194 if (content instanceof Course) 195 { 196 return (Course) content; 197 } 198 return null; 199 } 200 201 /** 202 * Get the target programs 203 * @param event the event 204 * @return the target programs in a List 205 * @throws Exception if failed to get content 206 */ 207 protected Set<Program> _getPrograms(Event event) throws Exception 208 { 209 Content content = _getContentArgument(event); 210 211 // ODF-1825 We should invalidate the cache for any program or course item because it can impacts the display of any other ODF content 212 213 if (content instanceof Program) 214 { 215 return Collections.singleton((Program) content); 216 } 217 else if (content instanceof ProgramItem) 218 { 219 return ((ProgramItem) content).getRootPrograms(); 220 } 221 else 222 { 223 getLogger().debug("This observer only handles ODF contents for virtual pages"); 224 return Collections.emptySet(); 225 } 226 } 227} 228