001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * 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. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.ui.core;
020:
021: import EDU.oswego.cs.dl.util.concurrent.SynchronizedInt;
022: import java.io.File;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.sql.Connection;
026: import java.sql.SQLException;
027: import java.util.Date;
028: import java.util.Properties;
029: import javax.naming.InitialContext;
030: import javax.naming.NamingException;
031: import javax.servlet.ServletContext;
032: import javax.servlet.ServletContextEvent;
033: import javax.servlet.ServletContextListener;
034: import javax.servlet.http.HttpSessionEvent;
035: import javax.sql.DataSource;
036: import org.acegisecurity.providers.ProviderManager;
037: import org.acegisecurity.providers.dao.DaoAuthenticationProvider;
038: import org.acegisecurity.providers.encoding.Md5PasswordEncoder;
039: import org.acegisecurity.providers.encoding.PasswordEncoder;
040: import org.acegisecurity.providers.encoding.ShaPasswordEncoder;
041: import org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint;
042: import org.apache.commons.lang.StringUtils;
043: import org.apache.commons.logging.Log;
044: import org.apache.commons.logging.LogFactory;
045: import org.apache.roller.RollerException;
046: import org.apache.roller.business.runnable.RollerTask;
047: import org.apache.roller.business.utils.UpgradeDatabase;
048: import org.apache.roller.config.PingConfig;
049: import org.apache.roller.config.RollerConfig;
050: import org.apache.roller.business.Roller;
051: import org.apache.roller.business.RollerFactory;
052: import org.apache.roller.business.runnable.ThreadManager;
053: import org.apache.roller.ui.core.plugins.UIPluginManager;
054: import org.apache.roller.ui.core.plugins.UIPluginManagerImpl;
055: import org.apache.roller.ui.core.security.AutoProvision;
056: import org.apache.roller.util.cache.CacheManager;
057: import org.apache.velocity.runtime.RuntimeSingleton;
058: import org.springframework.context.ApplicationContext;
059: import org.springframework.web.context.ContextLoaderListener;
060: import org.springframework.web.context.support.WebApplicationContextUtils;
061:
062: /**
063: * Responds to app init/destroy events and holds Roller instance.
064: *
065: * @web.listener
066: */
067: public class RollerContext extends ContextLoaderListener implements
068: ServletContextListener {
069:
070: private static Log mLogger = LogFactory.getLog(RollerContext.class);
071:
072: private String mVersion = null;
073: private String mBuildTime = null;
074: private String mBuildUser = null;
075:
076: public static final String ROLLER_CONTEXT = "roller.context";
077:
078: private static ServletContext mContext = null;
079: private static Authenticator mAuthenticator = null;
080: private final SynchronizedInt mSessionCount = new SynchronizedInt(0);
081:
082: /**
083: * Constructor for RollerContext.
084: */
085: public RollerContext() {
086: super ();
087:
088: Properties props = new Properties();
089: try {
090: props.load(getClass().getResourceAsStream(
091: "/version.properties"));
092: } catch (IOException e) {
093: mLogger.error("version.properties not found", e);
094: }
095:
096: mVersion = props.getProperty("ro.version", "UNKNOWN");
097: mBuildTime = props.getProperty("ro.buildTime", "UNKNOWN");
098: mBuildUser = props.getProperty("ro.buildUser", "UNKNOWN");
099: }
100:
101: /* Returns Roller instance for specified app */
102: public static RollerContext getRollerContext() {
103: // get roller from servlet context
104: ServletContext sc = RollerContext.getServletContext();
105: return (RollerContext) sc.getAttribute(ROLLER_CONTEXT);
106: }
107:
108: /** Responds to app-destroy by saving the indexManager's information */
109: public void contextDestroyed(ServletContextEvent sce) {
110: RollerFactory.getRoller().shutdown();
111:
112: // do we need a more generic mechanism for presentation layer shutdown?
113: CacheManager.shutdown();
114: }
115:
116: /**
117: * Responds to context initialization event by processing context
118: * paramters for easy access by the rest of the application.
119: */
120: public void contextInitialized(ServletContextEvent sce) {
121:
122: try {
123: Class.forName("org.hibernate.Session");
124: } catch (Throwable t) {
125: // if Hibernate is not available, we're hosed
126: throw new RuntimeException(
127: "FATAL ERROR: Hibernate not found, please refer to the Roller Installation Guide for instructions on how to install the required Hibernate jars");
128: }
129:
130: mLogger.debug("RollerContext initializing");
131:
132: // Save context in self and self in context
133: mContext = sce.getServletContext();
134: mContext.setAttribute(ROLLER_CONTEXT, this );
135:
136: // get the *real* path to <context>/resources
137: String ctxPath = mContext.getRealPath("/");
138: if (!ctxPath.endsWith(File.separator))
139: ctxPath += File.separator + "resources";
140: else
141: ctxPath += "resources";
142:
143: // try setting the uploads path to <context>/resources
144: // NOTE: this should go away at some point
145: // we leave it here for now to allow users to keep writing
146: // uploads into their webapp context, but this is a bad idea
147: //
148: // also, the RollerConfig.setUploadsDir() method is smart
149: // enough to disregard this call unless the uploads.path
150: // is set to ${webapp.context}
151: RollerConfig.setUploadsDir(ctxPath);
152:
153: // try setting the themes path to <context>/themes
154: // NOTE: this should go away at some point
155: // we leave it here for now to allow users to keep using
156: // themes in their webapp context, but this is a bad idea
157: //
158: // also, the RollerConfig.setThemesDir() method is smart
159: // enough to disregard this call unless the themes.dir
160: // is set to ${webapp.context}
161: RollerConfig.setThemesDir(mContext.getRealPath("/")
162: + File.separator + "themes");
163:
164: try {
165: // always upgrade database first
166: upgradeDatabaseIfNeeded();
167:
168: Roller roller = RollerFactory.getRoller();
169:
170: setupRollerProperties();
171:
172: // call Spring's context ContextLoaderListener to initialize
173: // all the context files specified in web.xml. This is necessary
174: // because listeners don't initialize in the order specified in
175: // 2.3 containers
176: super .contextInitialized(sce);
177:
178: initializeSecurityFeatures(mContext);
179:
180: setupVelocity();
181: roller.getThemeManager();
182: setupIndexManager(roller);
183: initializePingFeatures(roller);
184: setupTasks();
185:
186: roller.flush();
187: roller.release();
188:
189: } catch (Throwable t) {
190: mLogger.fatal("RollerContext initialization failed", t);
191: }
192:
193: mLogger.debug("RollerContext initialization complete");
194: }
195:
196: private void setupVelocity() throws RollerException {
197:
198: mLogger.info("Initializing Velocity");
199:
200: // initialize the Velocity engine
201: Properties velocityProps = new Properties();
202:
203: try {
204: InputStream instream = mContext
205: .getResourceAsStream("/WEB-INF/velocity.properties");
206:
207: velocityProps.load(instream);
208:
209: // need to dynamically add old macro libraries if they are enabled
210: if (RollerConfig
211: .getBooleanProperty("rendering.legacyModels.enabled")) {
212: String macroLibraries = (String) velocityProps
213: .get("velocimacro.library");
214: String oldLibraries = RollerConfig
215: .getProperty("velocity.oldMacroLibraries");
216:
217: // set the new value
218: velocityProps.setProperty("velocimacro.library",
219: oldLibraries + "," + macroLibraries);
220: }
221:
222: mLogger.debug("Velocity props = " + velocityProps);
223:
224: // init velocity
225: RuntimeSingleton.init(velocityProps);
226:
227: } catch (Exception e) {
228: throw new RollerException(e);
229: }
230:
231: }
232:
233: private void setupRollerProperties() throws RollerException {
234: // init property manager by creating it
235: Roller mRoller = RollerFactory.getRoller();
236: mRoller.getPropertiesManager();
237: }
238:
239: private void setupTasks() throws RollerException {
240:
241: ThreadManager tmgr = RollerFactory.getRoller()
242: .getThreadManager();
243:
244: Date now = new Date();
245:
246: // okay, first we look for what tasks have been enabled
247: String tasksStr = RollerConfig.getProperty("tasks.enabled");
248: String[] tasks = StringUtils.stripAll(StringUtils.split(
249: tasksStr, ","));
250: for (int i = 0; i < tasks.length; i++) {
251:
252: String taskClassName = RollerConfig.getProperty("tasks."
253: + tasks[i] + ".class");
254: if (taskClassName != null) {
255: mLogger.info("Initializing task: " + tasks[i]);
256:
257: try {
258: Class taskClass = Class.forName(taskClassName);
259: RollerTask task = (RollerTask) taskClass
260: .newInstance();
261: task.init();
262:
263: Date startTime = task.getStartTime(now);
264: if (startTime == null || now.after(startTime)) {
265: startTime = now;
266: }
267:
268: // schedule it
269: tmgr.scheduleFixedRateTimerTask(task, startTime,
270: task.getInterval());
271:
272: } catch (ClassCastException ex) {
273: mLogger
274: .warn(
275: "Task does not extend RollerTask class",
276: ex);
277: } catch (RollerException ex) {
278: mLogger.error("Error scheduling task", ex);
279: } catch (Exception ex) {
280: mLogger.error("Error instantiating task", ex);
281: }
282: }
283: }
284: }
285:
286: // Initialize ping features
287: private void initializePingFeatures(Roller roller)
288: throws RollerException {
289:
290: // Initialize common targets from the configuration
291: PingConfig.initializeCommonTargets();
292: // Initialize ping variants
293: PingConfig.initializePingVariants();
294: // Remove custom ping targets if they have been disallowed
295: if (PingConfig.getDisallowCustomTargets()) {
296: mLogger
297: .info("Custom ping targets have been disallowed. Removing any existing custom targets.");
298: roller.getPingTargetManager().removeAllCustomPingTargets();
299: }
300: // Remove all autoping configurations if ping usage has been disabled.
301: if (PingConfig.getDisablePingUsage()) {
302: mLogger
303: .info("Ping usage has been disabled. Removing any existing auto ping configurations.");
304: roller.getAutopingManager().removeAllAutoPings();
305: }
306: }
307:
308: protected void initializeSecurityFeatures(ServletContext context) {
309:
310: ApplicationContext ctx = WebApplicationContextUtils
311: .getRequiredWebApplicationContext(context);
312:
313: String rememberMe = RollerConfig
314: .getProperty("rememberme.enabled");
315: boolean rememberMeEnabled = Boolean.valueOf(rememberMe)
316: .booleanValue();
317:
318: mLogger.info("Remember Me enabled: " + rememberMeEnabled);
319:
320: context.setAttribute("rememberMeEnabled", rememberMe);
321:
322: if (rememberMeEnabled) {
323: ProviderManager provider = (ProviderManager) ctx
324: .getBean("authenticationManager");
325: provider.getProviders().add(
326: ctx.getBean("rememberMeAuthenticationProvider"));
327: }
328:
329: String encryptPasswords = RollerConfig
330: .getProperty("passwds.encryption.enabled");
331: boolean doEncrypt = Boolean.valueOf(encryptPasswords)
332: .booleanValue();
333:
334: if (doEncrypt) {
335: DaoAuthenticationProvider provider = (DaoAuthenticationProvider) ctx
336: .getBean("daoAuthenticationProvider");
337: String algorithm = RollerConfig
338: .getProperty("passwds.encryption.algorithm");
339: PasswordEncoder encoder = null;
340: if (algorithm.equalsIgnoreCase("SHA")) {
341: encoder = new ShaPasswordEncoder();
342: } else if (algorithm.equalsIgnoreCase("MD5")) {
343: encoder = new Md5PasswordEncoder();
344: } else {
345: mLogger.error("Encryption algorithm '" + algorithm
346: + "' not supported, disabling encryption.");
347: }
348: if (encoder != null) {
349: provider.setPasswordEncoder(encoder);
350: mLogger.info("Password Encryption Algorithm set to '"
351: + algorithm + "'");
352: }
353: }
354:
355: if (RollerConfig.getBooleanProperty("securelogin.enabled")) {
356: AuthenticationProcessingFilterEntryPoint entryPoint = (AuthenticationProcessingFilterEntryPoint) ctx
357: .getBean("authenticationProcessingFilterEntryPoint");
358: entryPoint.setForceHttps(true);
359: }
360: /*
361: if (RollerConfig.getBooleanProperty("schemeenforcement.enabled")) {
362:
363: ChannelProcessingFilter procfilter =
364: (ChannelProcessingFilter)ctx.getBean("channelProcessingFilter");
365: ConfigAttributeDefinition secureDef = new ConfigAttributeDefinition();
366: secureDef.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
367: ConfigAttributeDefinition insecureDef = new ConfigAttributeDefinition();
368: insecureDef.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
369: PathBasedFilterInvocationDefinitionMap defmap =
370: (PathBasedFilterInvocationDefinitionMap)procfilter.getFilterInvocationDefinitionSource();
371:
372: // add HTTPS URL path patterns to Acegi config
373: String httpsUrlsProp = RollerConfig.getProperty("schemeenforcement.https.urls");
374: if (httpsUrlsProp != null) {
375: String[] httpsUrls = StringUtils.stripAll(StringUtils.split(httpsUrlsProp, ",") );
376: for (int i=0; i<httpsUrls.length; i++) {
377: defmap.addSecureUrl(httpsUrls[i], secureDef);
378: }
379: }
380: // all other action URLs are non-HTTPS
381: defmap.addSecureUrl("/**<!-- need to remove this when uncommenting -->/*.do*", insecureDef);
382: }
383: */
384: }
385:
386: protected void upgradeDatabaseIfNeeded() throws RollerException {
387:
388: try {
389: InitialContext ic = new InitialContext();
390: DataSource ds = (DataSource) ic
391: .lookup("java:comp/env/jdbc/rollerdb");
392: Connection con = ds.getConnection();
393: UpgradeDatabase.upgradeDatabase(con, mVersion);
394: con.close();
395: } catch (NamingException e) {
396: mLogger.warn("Unable to access DataSource", e);
397: } catch (SQLException e) {
398: mLogger.warn(e);
399: }
400: }
401:
402: private void setupIndexManager(Roller roller)
403: throws RollerException {
404: roller.getIndexManager();
405: }
406:
407: public void sessionCreated(HttpSessionEvent se) {
408: mSessionCount.increment();
409:
410: mLogger.debug("sessions=" + mSessionCount + ":freemem="
411: + Runtime.getRuntime().freeMemory() + ":totmem="
412: + Runtime.getRuntime().totalMemory());
413: }
414:
415: public void sessionDestroyed(HttpSessionEvent se) {
416: mSessionCount.decrement();
417:
418: mLogger.debug("sessions=" + mSessionCount + ":freemem="
419: + Runtime.getRuntime().freeMemory() + ":totalmem="
420: + Runtime.getRuntime().totalMemory());
421: }
422:
423: /**
424: * Get authenticator
425: */
426: public Authenticator getAuthenticator() {
427: if (mAuthenticator == null) {
428: try {
429: Class authClass = Class.forName(RollerConfig
430: .getProperty("authenticator.classname"));
431: mAuthenticator = (Authenticator) authClass
432: .newInstance();
433: } catch (Exception e) {
434: // this isn't an ERROR if no authenticatorClass was specified
435: if (!(e instanceof NullPointerException)) {
436: mLogger
437: .error(
438: "ERROR creating authenticator, using default",
439: e);
440: } else {
441: mLogger
442: .debug("No authenticator specified, using DefaultAuthenticator");
443: }
444: mAuthenticator = new DefaultAuthenticator();
445: }
446: }
447: return mAuthenticator;
448: }
449:
450: /**
451: * Get the ServletContext.
452: *
453: * @return ServletContext
454: */
455: public static ServletContext getServletContext() {
456: return mContext;
457: }
458:
459: /** Roller version */
460: public String getRollerVersion() {
461: return mVersion;
462: }
463:
464: /** Roller build time */
465: public String getRollerBuildTime() {
466: return mBuildTime;
467: }
468:
469: /** Get username that built Roller */
470: public String getRollerBuildUser() {
471: return mBuildUser;
472: }
473:
474: /**
475: * Get an instance of AutoProvision, if available in roller.properties
476: *
477: * @return AutoProvision
478: */
479: public static AutoProvision getAutoProvision() {
480:
481: String clazzName = RollerConfig
482: .getProperty("users.sso.autoProvision.className");
483:
484: if (null == clazzName) {
485: return null;
486: }
487:
488: Class clazz;
489: try {
490: clazz = Class.forName(clazzName);
491: } catch (ClassNotFoundException e) {
492: mLogger.warn(
493: "Unable to found specified Auto Provision class.",
494: e);
495: return null;
496: }
497:
498: if (null == clazz) {
499: return null;
500: }
501:
502: Class[] interfaces = clazz.getInterfaces();
503: for (int i = 0; i < interfaces.length; i++) {
504: if (interfaces[i].equals(AutoProvision.class)) {
505: try {
506: return (AutoProvision) clazz.newInstance();
507: } catch (InstantiationException e) {
508: mLogger.warn(
509: "InstantiationException while creating: "
510: + clazzName, e);
511: } catch (IllegalAccessException e) {
512: mLogger.warn(
513: "IllegalAccessException while creating: "
514: + clazzName, e);
515: }
516: }
517: }
518:
519: return null;
520:
521: }
522:
523: /**
524: * Access to the plugin manager for the UI layer.
525: *
526: * TODO: we may want something similar to the Roller interface for the
527: * ui layer if we dont' want methods like this here in RollerContext.
528: */
529: public static UIPluginManager getUIPluginManager() {
530: // TODO: we may want to do this another way
531: return UIPluginManagerImpl.getInstance();
532: }
533:
534: }
|