<commit_before>package rundeck
import com.dtolabs.rundeck.core.common.IRundeckProject
import com.dtolabs.rundeck.core.common.ProjectManager
import com.dtolabs.rundeck.core.common.ProjectNodeSupport
import com.dtolabs.rundeck.core.storage.StorageTree
import com.dtolabs.rundeck.core.storage.StorageUtil
import com.dtolabs.rundeck.core.utils.IPropertyLookup
import com.dtolabs.rundeck.core.utils.PropertyLookup
import com.dtolabs.rundeck.server.projects.RundeckProject
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.ListenableFutureTask
import grails.transaction.Transactional
import org.rundeck.storage.data.DataUtil
import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import rundeck.services.FrameworkService

import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

@Transactional
class ProjectManagerService implements ProjectManager, ApplicationContextAware, InitializingBean {
    def FrameworkService frameworkService
    private StorageTree rundeckConfigStorageTree
    ApplicationContext applicationContext
    def grailsApplication

    private StorageTree getStorage(){
        if(null==rundeckConfigStorageTree){
            rundeckConfigStorageTree=applicationContext.getBean("rundeckConfigStorageTree",StorageTree)
        }
        return rundeckConfigStorageTree
    }

    @Override
    Collection<IRundeckProject> listFrameworkProjects() {
        return Project.findAll().collect{
            getFrameworkProject(it.name)
        }
    }

    @Override
    IRundeckProject getFrameworkProject(final String name) {
        if(!existsFrameworkProject(name)){
            throw new IllegalArgumentException("Project does not exist: "+name)
        }
        def result = projectCache.get(name)
        if(!result){
            throw new IllegalArgumentException("Project does not exist: "+name)
        }
        return result
    }

    @Override
    boolean existsFrameworkProject(final String project) {
        return Project.findByName(project)?true:false
    }

    @Override
    IRundeckProject createFrameworkProject(final String projectName) {
        return createFrameworkProject(projectName, new Properties())
    }
    /**
     * Scheduled executor for retries
     */
    private ExecutorService executor = Executors.newFixedThreadPool(2)

    //basic creation, created via spec string in afterPropertiesSet()
    private LoadingCache<String, IRundeckProject> projectCache =
            CacheBuilder.newBuilder()
                        .expireAfterAccess(10, TimeUnit.MINUTES)
                        .refreshAfterWrite(1, TimeUnit.MINUTES)
                        .build(
                    new CacheLoader<String, IRundeckProject>() {
                        public IRundeckProject load(String key) {
                            return loadProject(key);
                        }
                    }
            );

    @Override
    void afterPropertiesSet() throws Exception {
        def spec = grailsApplication.config.rundeck?.projectManagerService?.projectCache?.spec ?:
                "expireAfterAccess=10m,refreshAfterWrite=1m"


        projectCache = CacheBuilder.from(spec)
                                   .recordStats()
                                   .build(
                new CacheLoader<String, IRundeckProject>() {
                    public IRundeckProject load(String key) {
                        return loadProject(key);
                    }

                    @Override
                    ListenableFuture<IRundeckProject> reload(final String key, final IRundeckProject oldValue)
                            throws Exception
                    {
                        if (needsReload(oldValue)) {
                            ListenableFutureTask<IRundeckProject> task = ListenableFutureTask.create(
                                    new Callable<IRundeckProject>() {
                                        public IRundeckProject call() {
                                            return loadProject(key);
                                        }
                                    }
                            );
                            executor.execute(task);
                            return task;
                        } else {
                            return Futures.immediateFuture(oldValue)
                        }
                    }
                }
        )
    }

    /**
     * Loads or returns cached properties
     * @param projectName
     * @return
     */
    public Properties getProjectConfig(String projectName) {
        projectCache.get(projectName).getProperties()
    }

    Date getProjectConfigLastModified(String projectName) {

        def storagePath = "projects/" + projectName + "/etc/project.properties"<commit_msg>Add cache stats to metrics<commit_after>package rundeck

import com.codahale.metrics.Gauge
import com.codahale.metrics.MetricRegistry
import com.dtolabs.rundeck.core.common.IRundeckProject
import com.dtolabs.rundeck.core.common.ProjectManager
import com.dtolabs.rundeck.core.common.ProjectNodeSupport
import com.dtolabs.rundeck.core.storage.StorageTree
import com.dtolabs.rundeck.core.storage.StorageUtil
import com.dtolabs.rundeck.core.utils.IPropertyLookup
import com.dtolabs.rundeck.core.utils.PropertyLookup
import com.dtolabs.rundeck.server.projects.RundeckProject
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.ListenableFutureTask
import grails.transaction.Transactional
import org.rundeck.storage.data.DataUtil
import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import rundeck.services.FrameworkService

import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

@Transactional
class ProjectManagerService implements ProjectManager, ApplicationContextAware, InitializingBean {
    def FrameworkService frameworkService
    private StorageTree rundeckConfigStorageTree
    ApplicationContext applicationContext
    def grailsApplication
    def metricService

    private StorageTree getStorage(){
        if(null==rundeckConfigStorageTree){
            rundeckConfigStorageTree=applicationContext.getBean("rundeckConfigStorageTree",StorageTree)
        }
        return rundeckConfigStorageTree
    }

    @Override
    Collection<IRundeckProject> listFrameworkProjects() {
        return Project.findAll().collect{
            getFrameworkProject(it.name)
        }
    }

    @Override
    IRundeckProject getFrameworkProject(final String name) {
        if(!existsFrameworkProject(name)){
            throw new IllegalArgumentException("Project does not exist: "+name)
        }
        def result = projectCache.get(name)
        if(!result){
            throw new IllegalArgumentException("Project does not exist: "+name)
        }
        return result
    }

    @Override
    boolean existsFrameworkProject(final String project) {
        return Project.findByName(project)?true:false
    }

    @Override
    IRundeckProject createFrameworkProject(final String projectName) {
        return createFrameworkProject(projectName, new Properties())
    }
    /**
     * Scheduled executor for retries
     */
    private ExecutorService executor = Executors.newFixedThreadPool(2)

    //basic creation, created via spec string in afterPropertiesSet()
    private LoadingCache<String, IRundeckProject> projectCache =
            CacheBuilder.newBuilder()
                        .expireAfterAccess(10, TimeUnit.MINUTES)
                        .refreshAfterWrite(1, TimeUnit.MINUTES)
                        .build(
                    new CacheLoader<String, IRundeckProject>() {
                        public IRundeckProject load(String key) {
                            return loadProject(key);
                        }
                    }
            );

    @Override
    void afterPropertiesSet() throws Exception {
        def spec = grailsApplication.config.rundeck?.projectManagerService?.projectCache?.spec ?:
                "expireAfterAccess=10m,refreshAfterWrite=1m"


        projectCache = CacheBuilder.from(spec)
                                   .recordStats()
                                   .build(
                new CacheLoader<String, IRundeckProject>() {
                    public IRundeckProject load(String key) {
                        return loadProject(key);
                    }

                    @Override
                    ListenableFuture<IRundeckProject> reload(final String key, final IRundeckProject oldValue)
                            throws Exception
                    {
                        if (needsReload(oldValue)) {
                            ListenableFutureTask<IRundeckProject> task = ListenableFutureTask.create(
                                    new Callable<IRundeckProject>() {
                                        public IRundeckProject call() {
                                            return loadProject(key);
                                        }
                                    }
                            );
                            executor.execute(task);
                            return task;
                        } else {
                            return Futures.immediateFuture(oldValue)
                        }
                    }
                }
        )
        MetricRegistry registry=metricService.getMetricRegistry()
        registry.register(MetricRegistry.name(this.class.name+".projectCache", "hitCount"),new Gauge<Long>(){

            @Override
            Long getValue() {
                projectCache.stats().hitCount()
            }
        })
        registry.register(MetricRegistry.name(this.class.name+".projectCache", "evictionCount"),new Gauge<Long>(){

            @Override
            Long getValue() {
                projectCache.stats().evictionCount()
            }
        })
        registry.register(MetricRegistry.name(this.class.name+".projectCache", "missCount"),new Gauge<Long>(){

            @Override
            Long getValue() {
                projectCache.stats().missCount()
            }
        })
        registry.register(MetricRegistry.name(this.class.name+".projectCache", "loadExceptionCount"),new Gauge<Long>(){

            @Override
            Long getValue() {
                projectCache.stats().loadExceptionCount()
            }
        })
        registry.register(MetricRegistry.name(this.class.name+".projectCache", "hitRate"),new Gauge<Double>(){

            @Override
            Double getValue() {
                projectCache.stats().hitRate()
            }
        })
    }

    /**
     * Loads or returns cached properties
     * @param projectName
     * @return
     */
    public Properties getProjectConfig(String projectName) {
        projectCache.get(projectName).getProperties()
    }

    Date getProjectConfigLastModified(String projectName) {

        def storagePath = "projects/" + projectName + "/etc/project.properties"
{'fraction_non_alphanumeric': 0.07645311837081036, 'fraction_numerical': 0.0011879507848960543, 'mean_word_length': 2.0813071895424837, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3751081', 'n_tokens_mistral': 2838, 'n_tokens_neox': 2706, 'n_words': 502}