001/* 002 * Copyright (C) 2022 - 2023, the original author or authors. 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 io.github.ascopes.jct.filemanagers.config; 017 018import io.github.ascopes.jct.filemanagers.JctFileManager; 019import java.util.ArrayDeque; 020import java.util.Deque; 021import java.util.List; 022import java.util.concurrent.locks.Lock; 023import java.util.concurrent.locks.ReentrantLock; 024import org.apiguardian.api.API; 025import org.apiguardian.api.API.Status; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029/** 030 * A chain of configurers to apply to a file manager. 031 * 032 * <p>While not designed for concurrent use, all operations are shielded by a 033 * lock to guard against potentially confusing behaviour, should this component be shared across 034 * multiple physical and virtual threads. 035 * 036 * @author Ashley Scopes 037 * @since 0.0.1 038 */ 039@API(since = "0.0.1", status = Status.STABLE) 040public final class JctFileManagerConfigurerChain { 041 042 private static final Logger LOGGER = LoggerFactory.getLogger(JctFileManagerConfigurerChain.class); 043 044 private final Lock lock; 045 private final Deque<JctFileManagerConfigurer> configurers; 046 047 /** 048 * Initialise this chain. 049 */ 050 public JctFileManagerConfigurerChain() { 051 lock = new ReentrantLock(); 052 configurers = new ArrayDeque<>(16); 053 } 054 055 /** 056 * Add a configurer to the start of the chain. 057 * 058 * @param configurer the configurer to add. 059 * @return this chain for further calls. 060 */ 061 public JctFileManagerConfigurerChain addFirst(JctFileManagerConfigurer configurer) { 062 lock.lock(); 063 try { 064 configurers.addFirst(configurer); 065 } finally { 066 lock.unlock(); 067 } 068 return this; 069 } 070 071 /** 072 * Add a configurer to the end of the chain. 073 * 074 * @param configurer the configurer to add. 075 * @return this chain for further calls. 076 */ 077 public JctFileManagerConfigurerChain addLast(JctFileManagerConfigurer configurer) { 078 lock.lock(); 079 try { 080 configurers.addLast(configurer); 081 } finally { 082 lock.unlock(); 083 } 084 return this; 085 } 086 087 /** 088 * Get an immutable copy of the list of configurers. 089 * 090 * @return the list of configurers. 091 */ 092 public List<JctFileManagerConfigurer> list() { 093 lock.lock(); 094 try { 095 return List.copyOf(configurers); 096 } finally { 097 lock.unlock(); 098 } 099 } 100 101 /** 102 * Apply each configurer to the given file manager in order. 103 * 104 * @param fileManager the file manager to configure. 105 * @return the configured file manager to use. This may or may not be the same object as the input 106 * parameter, depending on how the configurers manipulate the input object. 107 */ 108 public JctFileManager configure(JctFileManager fileManager) { 109 lock.lock(); 110 try { 111 for (var configurer : configurers) { 112 if (configurer.isEnabled()) { 113 LOGGER.debug("Applying {} to file manager", configurer); 114 fileManager = configurer.configure(fileManager); 115 } else { 116 LOGGER.trace("Skipping {}", configurer); 117 } 118 } 119 } finally { 120 lock.unlock(); 121 } 122 123 return fileManager; 124 } 125}