/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import com.enonic.cms.core.structure.SiteKey;
import com.enonic.cms.core.structure.SitePropertiesServiceImpl;
import com.enonic.cms.core.vhost.VirtualHostResolver;
@Service
public class ConfigFilesWatcherService
{
private static final Logger LOG = LoggerFactory.getLogger( ConfigFilesWatcherService.class );
public static final String SITE_PROPERTIES_FILENAME = "site-(\\d+).properties";
public static final String[] EMPTY_DIR = {};
@Autowired
private VirtualHostResolver virtualHostResolver;
@Autowired
private SitePropertiesServiceImpl sitePropertiesService;
private File configDir;
private boolean configDirObserved;
private Map<String, Long> lastModified;
public ConfigFilesWatcherService()
{
lastModified = new HashMap<String, Long>();
}
@PostConstruct
public void initService()
{
configDirObserved = configDir.exists();
if ( configDirObserved )
{
final String[] filenames = configDir.list();
resolveLastModified( filenames );
}
}
private String[] listConfigFiles()
{
final String[] filenames = configDir.list();
checkConfigDirObserved( filenames );
return filenames != null ? filenames : EMPTY_DIR;
}
private void checkConfigDirObserved( final String[] filenames )
{
if ( filenames != null && !configDirObserved )
{
resolveLastModified( filenames );
markObservedConfigFilesAsModified();
LOG.debug( "config directory was created." );
}
else if ( filenames == null && configDirObserved )
{
markObservedConfigFilesAsModified();
LOG.debug( "config directory was removed." );
}
configDirObserved = filenames != null;
}
private void markObservedConfigFilesAsModified()
{
for ( final Map.Entry<String, Long> entry : lastModified.entrySet() )
{
entry.setValue( 0L );
}
}
private void resolveLastModified( final String[] filenames )
{
lastModified.clear();
for ( final String filename : filenames )
{
if ( isObservableFile( filename ) )
{
final File file = new File( configDir.getAbsoluteFile() + File.separator + filename );
lastModified.put( filename, file.lastModified() );
}
}
}
private boolean isObservableFile( final String filename )
{
return filename.equals( "vhost.properties" ) || filename.matches( SITE_PROPERTIES_FILENAME ) ||
filename.equals( "cms.properties" );
}
/**
* used spring scheduler. Runs every 2 sec.
* <p/>
* monitors cms.properties, vhost.properties and site-*.properties
*/
@Scheduled(fixedRate = 2000)
private void checkConfigFiles()
{
final String[] dir = listConfigFiles();
final Set<String> filenames = new HashSet<String>();
filenames.addAll( Arrays.asList( dir ) );
filenames.addAll( lastModified.keySet() );
for ( final String filename : filenames )
{
if ( !isObservableFile( filename ) || !isFileModified( filename ) )
{
continue;
}
if ( filename.equals( "vhost.properties" ) )
{
virtualHostResolver.configureVirtualHosts();
LOG.info( "Reloaded vhost configuration." );
}
else if ( filename.matches( SITE_PROPERTIES_FILENAME ) )
{
final Pattern pattern = Pattern.compile( SITE_PROPERTIES_FILENAME );
final Matcher matcher = pattern.matcher( filename );
if ( matcher.matches() )
{
final SiteKey site = new SiteKey( matcher.group( 1 ) );
sitePropertiesService.reloadSiteProperties( site );
LOG.info( "Reloaded configuration for site #{}.", site );
}
}
else if ( filename.equals( "cms.properties" ) )
{
LOG.info( "{} was changed. Please restart the application to load new values.", filename );
}
}
}
private boolean isFileModified( final String filename )
{
final File file = new File( configDir.getAbsoluteFile() + File.separator + filename );
if ( !file.exists() )
{
// config file was removed
lastModified.remove( filename );
return true;
}
final long lastModifiedNow = file.lastModified();
final Long lastModifiedPrev = lastModified.put( filename, lastModifiedNow );
return lastModifiedPrev == null || lastModifiedNow > lastModifiedPrev; // new file or changed
}
@Value("${cms.home}/config")
public void setConfigDir( final File configDir )
{
this.configDir = configDir;
}
}