/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Sam */ package com.caucho.server.rewrite; import com.caucho.config.Config; import com.caucho.config.ConfigException; import com.caucho.config.types.Period; import com.caucho.loader.Environment; import com.caucho.management.server.RewriteImportMXBean; import com.caucho.util.Alarm; import com.caucho.util.AlarmListener; import com.caucho.util.L10N; import com.caucho.vfs.Depend; import com.caucho.vfs.Path; import com.caucho.jmx.Description; import javax.annotation.PostConstruct; import javax.servlet.FilterChain; import javax.servlet.ServletException; import java.util.logging.Level; import java.util.logging.Logger; public class ImportRule extends AbstractRule implements AlarmListener { private static L10N L = new L10N(ImportRule.class); private static Logger log = Logger.getLogger(ImportRule.class.getName()); private Path _path; private boolean _isOptional; private long _dependencyCheckInterval = 2 * 1000L; private long _errorCheckInterval = 2 * 1000L; private Depend _depend; private volatile MatchRule _matchRule; private volatile boolean _isError; private volatile String _redeployError; private volatile boolean _isDestroyed = false; private Alarm _alarm; protected ImportRule(RewriteDispatch rewriteDispatch) { super(rewriteDispatch); _dependencyCheckInterval = Environment.getDependencyCheckInterval(); } public String getTagName() { return "import"; } public void setDependencyCheckInterval(Period dependencyCheckInterval) { _dependencyCheckInterval = dependencyCheckInterval.getPeriod(); } public long getDependencyCheckInterval() { return _dependencyCheckInterval; } public String getRedeployError() { return _redeployError; } public void setOptional(boolean isOptional) { _isOptional = isOptional; } public void setPath(Path path) { _path = path; } @Override @PostConstruct public void init() { if (_path == null) throw new ConfigException(L.l("'path' attribute missing from resin:import.")); if (getName() == null) setName(_path.getNativePath()); try { load(); } catch (ConfigException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } super.init(); _alarm = new Alarm("rewrite-dispatch-import", this, _dependencyCheckInterval); } @Override protected RewriteRuleAdmin createAdmin() { return new RewriteImportAdmin(this); } public void setPassFilterChainMapper(FilterChainMapper passFilterChainMapper) { super.setPassFilterChainMapper(passFilterChainMapper); if (_matchRule != null) _matchRule.setPassFilterChainMapper(passFilterChainMapper); } public void setFailFilterChainMapper(FilterChainMapper failFilterChainMapper) { super.setFailFilterChainMapper(failFilterChainMapper); if (_matchRule != null) _matchRule.setFailFilterChainMapper(failFilterChainMapper); } public FilterChain map(String uri, String query, FilterChain accept) throws ServletException { if (isEnabled() && _matchRule != null) return _matchRule.map(uri, query, accept); else if (getPassFilterChainMapper() != null) return getPassFilterChainMapper().map(uri, query, accept); else return accept; } private synchronized void load() throws Exception { if (_isDestroyed) return; if (_depend != null && !_depend.isModified()) return; try { _depend = new Depend(_path); _isError = true; MatchRule matchRule = new MatchRule(getRewriteDispatch()); matchRule.setPassFilterChainMapper(getPassFilterChainMapper()); matchRule.setFailFilterChainMapper(getFailFilterChainMapper()); if (_path.canRead() && ! _path.isDirectory()) { } else if (_isOptional) { log.finer(L.l("import '{0}' is not readable.", _path)); matchRule.init(); _isError = false; setMatchRule(matchRule); return; } else { throw new ConfigException(L.l("Required file '{0}' can not be read for import.", _path.getNativePath())); } Config config = new Config(); config.configure(matchRule, _path); _isError = false; setMatchRule(matchRule); } finally { clearCache(); } } private void setMatchRule(MatchRule matchRule) { MatchRule oldMatchRule = _matchRule; if (oldMatchRule != null) oldMatchRule.unregister(); matchRule.register(); _matchRule = matchRule; if (oldMatchRule != null) oldMatchRule.destroy(); } public void handleAlarm(Alarm alarm) { alarm = _alarm; if (_isDestroyed) return; try { _redeployError = null; load(); } catch (Exception ex) { _redeployError = ex.toString(); log.log(Level.WARNING, ex.toString(), ex); } finally { if (! _isDestroyed) { long delta = _isError ? _errorCheckInterval : _dependencyCheckInterval; alarm.queue(delta); } } } private void update() { handleAlarm(null); } @Override public void destroy() { try { _isDestroyed = true; Alarm alarm = _alarm; _alarm = null; MatchRule matchRule = _matchRule; _matchRule = null; if (alarm != null) alarm.dequeue(); if (matchRule != null) matchRule.destroy(); } finally { super.destroy(); } } public static class RewriteImportAdmin extends RewriteRuleAdmin implements RewriteImportMXBean { private final ImportRule _rule; public RewriteImportAdmin(ImportRule rule) { super(rule); _rule = rule; } @Override public String getType() { return "RewriteImport"; } public long getDependencyCheckInterval() { return _rule.getDependencyCheckInterval(); } public String getRedeployError() { return _rule.getRedeployError(); } @Description("Updates the imported rules if the file has changed") public void update() { _rule.update(); } } }