001/* 002 * Copyright 2019 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.odf.schedulable; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.excalibur.xml.dom.DOMParser; 033import org.apache.excalibur.xml.xpath.XPathProcessor; 034import org.quartz.JobKey; 035import org.quartz.SchedulerException; 036import org.w3c.dom.Document; 037import org.w3c.dom.NamedNodeMap; 038import org.w3c.dom.Node; 039import org.w3c.dom.NodeList; 040import org.xml.sax.InputSource; 041import org.xml.sax.SAXException; 042 043import org.ametys.core.schedule.Runnable; 044import org.ametys.core.schedule.Runnable.FireProcess; 045import org.ametys.core.schedule.Runnable.MisfirePolicy; 046import org.ametys.core.ui.Callable; 047import org.ametys.core.user.CurrentUserProvider; 048import org.ametys.plugins.core.impl.schedule.DefaultRunnable; 049import org.ametys.plugins.core.schedule.Scheduler; 050import org.ametys.runtime.i18n.I18nizableText; 051import org.ametys.runtime.plugin.component.AbstractLogEnabled; 052import org.ametys.runtime.util.AmetysHomeHelper; 053 054/** 055 * The component for scheduling a global validation report on programs 056 */ 057public class GlobalValidationReport extends AbstractLogEnabled implements Component, Serviceable 058{ 059 /** The Avalon role */ 060 public static final String ROLE = GlobalValidationReport.class.getName(); 061 062 private static final String __SCHEDULABLE_ID = "org.ametys.odf.schedulable.GlobalValidationSchedulable"; 063 private static final String __JOB_KEY = __SCHEDULABLE_ID + "$task"; 064 065 private Scheduler _scheduler; 066 private CurrentUserProvider _userProvider; 067 068 private XPathProcessor _xPathProcessor; 069 070 private DOMParser _domParser; 071 072 public void service(ServiceManager smanager) throws ServiceException 073 { 074 _scheduler = (Scheduler) smanager.lookup(Scheduler.ROLE); 075 _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 076 _domParser = (DOMParser) smanager.lookup(DOMParser.ROLE); 077 _xPathProcessor = (XPathProcessor) smanager.lookup(XPathProcessor.ROLE); 078 } 079 080 /** 081 * Determines if the scheduler is currently running 082 * @return true if a check is already running 083 */ 084 @Callable 085 public boolean isRunning() 086 { 087 return Boolean.TRUE.equals(_scheduler.isRunning(__JOB_KEY).get("running")); 088 } 089 090 /** 091 * Start the global validation report on programs 092 * @return the result 093 */ 094 @Callable 095 public Map<String, Object> startReport() 096 { 097 Map<String, Object> result = new HashMap<>(); 098 099 try 100 { 101 Runnable runnable = new DefaultRunnable(__JOB_KEY, 102 new I18nizableText("plugin.odf", "PLUGINS_ODF_SCHEDULABLE_GLOBAL_VALIDATION_REPORT_LABEL"), 103 new I18nizableText("plugin.odf", "PLUGINS_ODF_SCHEDULABLE_GLOBAL_VALIDATION_REPORT_DESCRIPTION"), 104 FireProcess.NOW, 105 null /* cron*/, 106 __SCHEDULABLE_ID, 107 false /* removable */, 108 false /* modifiable */, 109 false /* deactivatable */, 110 MisfirePolicy.FIRE_ONCE, 111 false /* isVolatile */, 112 _userProvider.getUser(), 113 Collections.EMPTY_MAP); 114 115 if (isRunning()) 116 { 117 getLogger().warn("A global validation check is already running"); 118 result.put("error", "already-running"); 119 result.put("success", false); 120 } 121 else 122 { 123 JobKey jobKey = new JobKey(runnable.getId(), Scheduler.JOB_GROUP); 124 125 if (_scheduler.getScheduler().checkExists(jobKey)) 126 { 127 _scheduler.getScheduler().deleteJob(jobKey); 128 } 129 _scheduler.scheduleJob(runnable); 130 result.put("success", true); 131 } 132 133 return result; 134 } 135 catch (SchedulerException e) 136 { 137 getLogger().error("An error occured when trying to schedule the global validation status on programs", e); 138 result.put("success", false); 139 return result; 140 } 141 } 142 143 /** 144 * Get the list of invalidated contents computed by the last report for a given content 145 * @param contentId the content id 146 * @return the list of invalidated contents 147 */ 148 @Callable 149 public List<Map<String, Object>> getInvalidatedContents(String contentId) 150 { 151 List<Map<String, Object>> invalidatedContents = new ArrayList<>(); 152 153 File reportFile = new File(AmetysHomeHelper.getAmetysHomeData(), "odf/report/global-validation.xml"); 154 155 if (reportFile.exists()) 156 { 157 try (InputStream is = new FileInputStream(reportFile)) 158 { 159 Document doc = _domParser.parseDocument(new InputSource(is)); 160 161 NodeList nodeList = _xPathProcessor.selectNodeList(doc, "/programs/program[@id='" + contentId + "']/invalidatedContents/content"); 162 163 for (int i = 0; i < nodeList.getLength(); i++) 164 { 165 Node node = nodeList.item(i); 166 invalidatedContents.add(_getAttributes(node)); 167 } 168 } 169 catch (IOException | SAXException e) 170 { 171 getLogger().error("Unable to get the list of invalidated contents computed by the last report for program '{}'", contentId, e); 172 } 173 } 174 175 return invalidatedContents; 176 } 177 178 private Map<String, Object> _getAttributes(Node node) 179 { 180 Map<String, Object> map = new HashMap<>(); 181 182 NamedNodeMap attributes = node.getAttributes(); 183 for (int i = 0; i < attributes.getLength(); i++) 184 { 185 Node attribute = attributes.item(i); 186 187 String name = attribute.getNodeName(); 188 String value = attribute.getNodeValue(); 189 190 map.put(name, value); 191 } 192 193 return map; 194 } 195}