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 if (!Config.getInstance().getValueAsBoolean("remind.content.enabled")) 095 { 096 return; 097 } 098 099 if (_logger.isDebugEnabled()) 100 { 101 _logger.debug("Initializing the alert scheduler..."); 102 } 103 104 // Schedule a timer to run each night. 105 String hourStr = Config.getInstance().getValueAsString("alerts.scheduler.hour"); 106 int hour = 0; 107 int minute = 0; 108 if (StringUtils.isNotEmpty(hourStr) && hourStr.indexOf(':') > 0) 109 { 110 String[] hourArray = StringUtils.split(hourStr, ':'); 111 hour = Integer.parseInt(hourArray[0]); 112 minute = Integer.parseInt(hourArray[1]); 113 } 114 115 GregorianCalendar calendar = new GregorianCalendar(); 116 calendar.set(Calendar.AM_PM, hour < 12 ? Calendar.AM : Calendar.PM); 117 calendar.set(Calendar.HOUR, hour % 12); 118 calendar.set(Calendar.MINUTE, minute); 119 calendar.set(Calendar.SECOND, 0); 120 calendar.set(Calendar.MILLISECOND, 0); 121 122 // Each day. 123 long period = 86400000; 124 Date firstTime = calendar.getTime(); 125 126 Date now = new Date(); 127 128 // If the given time today is past, schedule for tomorrow. 129 if (firstTime.compareTo(now) < 0) 130 { 131 calendar.add(Calendar.DAY_OF_MONTH, 1); 132 firstTime = calendar.getTime(); 133 } 134 135 if (_logger.isInfoEnabled()) 136 { 137 _logger.info("Alerts scheduler: the alerts engine will run each day, starting " + firstTime.toString()); 138 } 139 140 _timer = new Timer("AlertsScheduler", true); 141 _timer.schedule(this, firstTime, period); 142 } 143 144 @Override 145 public void run() 146 { 147 AlertEngine alertEngine = new AlertEngine(); 148 149 try 150 { 151 // Initialize and configure the engine. 152 alertEngine.initialize(_manager, _context); 153 alertEngine.configure(_configuration); 154 } 155 catch (Exception e) 156 { 157 throw new RuntimeException("Unable to initialize the alerts engine", e); 158 } 159 160 // The thread will be started as daemon, as the scheduler is marked daemon itself. 161 new Thread(alertEngine, "AlertEngine").start(); 162 } 163 164 /** 165 * Run the scheduler to send instant alerts on contents 166 * @param contentIds The ids of concerned contents 167 * @param message the message to send to authorized users 168 */ 169 public void sendInstantAlerts (List<String> contentIds, String message) 170 { 171 AlertEngine alertEngine = new AlertEngine(contentIds, message); 172 173 try 174 { 175 // Initialize and configure the engine. 176 alertEngine.initialize(_manager, _context); 177 alertEngine.configure(_configuration); 178 } 179 catch (Exception e) 180 { 181 throw new RuntimeException("Unable to initialize the alerts engine", e); 182 } 183 184 // The thread will be started as daemon, as the scheduler is marked daemon itself. 185 new Thread(alertEngine, "AlertEngine").start(); 186 } 187 188 /** 189 * Get the request URI. 190 * @param request the request object. 191 * @return the full request URI. 192 */ 193 protected String _getRequestURI(Request request) 194 { 195 StringBuilder sb = new StringBuilder(); 196 sb.append(request.getScheme()); 197 sb.append("://"); 198 sb.append(request.getServerName()); 199 200 // Construire une uri sans :80 en http et sans :443 en https 201 if (request.isSecure()) 202 { 203 if (request.getServerPort() != 443) 204 { 205 sb.append(":"); 206 sb.append(request.getServerPort()); 207 } 208 } 209 else 210 { 211 if (request.getServerPort() != 80) 212 { 213 sb.append(":"); 214 sb.append(request.getServerPort()); 215 } 216 } 217 218 sb.append(request.getContextPath()); 219 220 return sb.toString(); 221 } 222 223 @Override 224 public void dispose() 225 { 226 _context = null; 227 _logger = null; 228 _manager = null; 229 _configuration = null; 230 231 cancel(); 232 if (_timer != null) 233 { 234 _timer.cancel(); 235 _timer = null; 236 } 237 } 238 239}