001/* 002 * Copyright 2014 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.cms.alerts; 017 018import java.util.Calendar; 019import java.util.Date; 020import java.util.GregorianCalendar; 021import java.util.List; 022import java.util.Timer; 023import java.util.TimerTask; 024 025import org.apache.avalon.framework.activity.Disposable; 026import org.apache.avalon.framework.activity.Initializable; 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.configuration.Configurable; 029import org.apache.avalon.framework.configuration.Configuration; 030import org.apache.avalon.framework.configuration.ConfigurationException; 031import org.apache.avalon.framework.context.Context; 032import org.apache.avalon.framework.context.ContextException; 033import org.apache.avalon.framework.context.Contextualizable; 034import org.apache.avalon.framework.logger.LogEnabled; 035import org.apache.avalon.framework.logger.Logger; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.avalon.framework.service.Serviceable; 039import org.apache.cocoon.environment.Request; 040import org.apache.commons.lang.StringUtils; 041 042import org.ametys.runtime.config.Config; 043 044/** 045 * Alerts scheduler: launches a cron which sends the alerts each night. 046 */ 047public class AlertScheduler extends TimerTask implements Initializable, LogEnabled, Serviceable, Disposable, Contextualizable, Configurable, Component 048{ 049 /** The Avalon role */ 050 public static final String ROLE = AlertScheduler.class.getName(); 051 052 /** The service manager. */ 053 protected ServiceManager _manager; 054 055 /** The component configuration. */ 056 protected Configuration _configuration; 057 058 /** The avalon context. */ 059 protected Context _context; 060 061 /** The logger. */ 062 protected Logger _logger; 063 064 /** The timer. */ 065 protected Timer _timer; 066 067 @Override 068 public void service(ServiceManager manager) throws ServiceException 069 { 070 _manager = manager; 071 } 072 073 @Override 074 public void contextualize(Context context) throws ContextException 075 { 076 _context = context; 077 } 078 079 @Override 080 public void configure(Configuration configuration) throws ConfigurationException 081 { 082 _configuration = configuration; 083 } 084 085 @Override 086 public void enableLogging(Logger logger) 087 { 088 _logger = logger; 089 } 090 091 @Override 092 public void initialize() throws Exception 093 { 094 boolean enabled = Config.getInstance().getValue("remind.content.enabled"); 095 if (!enabled) 096 { 097 return; 098 } 099 100 if (_logger.isDebugEnabled()) 101 { 102 _logger.debug("Initializing the alert scheduler..."); 103 } 104 105 // Schedule a timer to run each night. 106 String hourStr = Config.getInstance().getValue("alerts.scheduler.hour"); 107 int hour = 0; 108 int minute = 0; 109 if (StringUtils.isNotEmpty(hourStr) && hourStr.indexOf(':') > 0) 110 { 111 String[] hourArray = StringUtils.split(hourStr, ':'); 112 hour = Integer.parseInt(hourArray[0]); 113 minute = Integer.parseInt(hourArray[1]); 114 } 115 116 GregorianCalendar calendar = new GregorianCalendar(); 117 calendar.set(Calendar.AM_PM, hour < 12 ? Calendar.AM : Calendar.PM); 118 calendar.set(Calendar.HOUR, hour % 12); 119 calendar.set(Calendar.MINUTE, minute); 120 calendar.set(Calendar.SECOND, 0); 121 calendar.set(Calendar.MILLISECOND, 0); 122 123 // Each day. 124 long period = 86400000; 125 Date firstTime = calendar.getTime(); 126 127 Date now = new Date(); 128 129 // If the given time today is past, schedule for tomorrow. 130 if (firstTime.compareTo(now) < 0) 131 { 132 calendar.add(Calendar.DAY_OF_MONTH, 1); 133 firstTime = calendar.getTime(); 134 } 135 136 if (_logger.isInfoEnabled()) 137 { 138 _logger.info("Alerts scheduler: the alerts engine will run each day, starting " + firstTime.toString()); 139 } 140 141 _timer = new Timer("AlertsScheduler", true); 142 _timer.schedule(this, firstTime, period); 143 } 144 145 @Override 146 public void run() 147 { 148 AlertEngine alertEngine = new AlertEngine(); 149 150 try 151 { 152 // Initialize and configure the engine. 153 alertEngine.initialize(_manager, _context); 154 alertEngine.configure(_configuration); 155 } 156 catch (Exception e) 157 { 158 throw new RuntimeException("Unable to initialize the alerts engine", e); 159 } 160 161 // The thread will be started as daemon, as the scheduler is marked daemon itself. 162 new Thread(alertEngine, "AlertEngine").start(); 163 } 164 165 /** 166 * Run the scheduler to send instant alerts on contents 167 * @param contentIds The ids of concerned contents 168 * @param message the message to send to authorized users 169 */ 170 public void sendInstantAlerts (List<String> contentIds, String message) 171 { 172 AlertEngine alertEngine = new AlertEngine(contentIds, message); 173 174 try 175 { 176 // Initialize and configure the engine. 177 alertEngine.initialize(_manager, _context); 178 alertEngine.configure(_configuration); 179 } 180 catch (Exception e) 181 { 182 throw new RuntimeException("Unable to initialize the alerts engine", e); 183 } 184 185 // The thread will be started as daemon, as the scheduler is marked daemon itself. 186 new Thread(alertEngine, "AlertEngine").start(); 187 } 188 189 /** 190 * Get the request URI. 191 * @param request the request object. 192 * @return the full request URI. 193 */ 194 protected String _getRequestURI(Request request) 195 { 196 StringBuilder sb = new StringBuilder(); 197 sb.append(request.getScheme()); 198 sb.append("://"); 199 sb.append(request.getServerName()); 200 201 // Construire une uri sans :80 en http et sans :443 en https 202 if (request.isSecure()) 203 { 204 if (request.getServerPort() != 443) 205 { 206 sb.append(":"); 207 sb.append(request.getServerPort()); 208 } 209 } 210 else 211 { 212 if (request.getServerPort() != 80) 213 { 214 sb.append(":"); 215 sb.append(request.getServerPort()); 216 } 217 } 218 219 sb.append(request.getContextPath()); 220 221 return sb.toString(); 222 } 223 224 @Override 225 public void dispose() 226 { 227 _context = null; 228 _logger = null; 229 _manager = null; 230 _configuration = null; 231 232 cancel(); 233 if (_timer != null) 234 { 235 _timer.cancel(); 236 _timer = null; 237 } 238 } 239 240}