/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.logstash.logback.appender; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import com.lmax.disruptor.BlockingWaitStrategy; import com.lmax.disruptor.BusySpinWaitStrategy; import com.lmax.disruptor.LiteBlockingWaitStrategy; import com.lmax.disruptor.PhasedBackoffWaitStrategy; import com.lmax.disruptor.SleepingWaitStrategy; import com.lmax.disruptor.TimeoutBlockingWaitStrategy; import com.lmax.disruptor.WaitStrategy; import com.lmax.disruptor.YieldingWaitStrategy; /** * Creates {@link WaitStrategy} objects from strings. */ public class WaitStrategyFactory { private static final char PARAM_END_CHAR = '}'; private static final char PARAM_START_CHAR = '{'; private static final char PARAM_SEPARATOR_CHAR = ','; /** * Creates a {@link WaitStrategy} from a string. * <p> * The following strategies are supported: * <p> * <ul> * <li><tt>blocking</tt> - {@link BlockingWaitStrategy}</li> * <li><tt>busySpin</tt> - {@link BusySpinWaitStrategy}</li> * <li><tt>liteBlocking</tt> - {@link LiteBlockingWaitStrategy}</li> * <li><tt>sleeping</tt> - {@link SleepingWaitStrategy}</li> * <li><tt>yielding</tt> - {@link YieldingWaitStrategy}</li> * <li><tt>phasedBackoff{spinTimeout,yieldTimeout,timeUnit,fallackStrategy}</tt> - {@link PhasedBackoffWaitStrategy} * - <tt>spinTimeout</tt> and <tt>yieldTimeout</tt> are long values. * <tt>timeUnit</tt> is a string name of one of the {@link TimeUnit} values. * <tt>fallbackStrategy</tt> is a wait strategy string (e.g. <tt>blocking</tt>). * </li> * <li><tt>timeoutBlocking{timeout,timeUnit}</tt> - {@link TimeoutBlockingWaitStrategy} * - <tt>timeout</tt> is a long value. * <tt>timeUnit</tt> is a string name of one of the {@link TimeUnit} values. * </li> * </ul> * * @throws IllegalArgumentException if an unknown wait strategy type is given, or the parameters are unable to be parsed. */ public static WaitStrategy createWaitStrategyFromString(String waitStrategyType) { if (waitStrategyType == null) { return null; } waitStrategyType = waitStrategyType.trim().toLowerCase(); if (waitStrategyType.isEmpty()) { return null; } if (waitStrategyType.equals("blocking")) { return new BlockingWaitStrategy(); } if (waitStrategyType.equals("busyspin")) { return new BusySpinWaitStrategy(); } if (waitStrategyType.equals("liteblocking")) { return new LiteBlockingWaitStrategy(); } if (waitStrategyType.equals("sleeping")) { return new SleepingWaitStrategy(); } if (waitStrategyType.equals("yielding")) { return new YieldingWaitStrategy(); } if (waitStrategyType.startsWith("phasedbackoff")) { List<Object> params = parseParams(waitStrategyType, Long.class, Long.class, TimeUnit.class, WaitStrategy.class); return new PhasedBackoffWaitStrategy( (Long) params.get(0), (Long) params.get(1), (TimeUnit) params.get(2), (WaitStrategy) params.get(3)); } if (waitStrategyType.startsWith("timeoutblocking")) { List<Object> params = parseParams(waitStrategyType, Long.class, TimeUnit.class); return new TimeoutBlockingWaitStrategy( (Long) params.get(0), (TimeUnit) params.get(1)); } throw new IllegalArgumentException("Unknown wait strategy type: " + waitStrategyType); } private static List<Object> parseParams(String waitStrategyType, Class<?>... paramTypes) { String paramsString = extractParamsString(waitStrategyType, paramTypes); List<Object> params = new ArrayList<Object>(paramTypes.length); int startIndex = 0; for (int i = 0; i < paramTypes.length && startIndex < paramsString.length(); i++) { int endIndex = findParamEndIndex(paramsString, startIndex); String paramString = paramsString.substring(startIndex, endIndex).trim(); startIndex = endIndex + 1; if (Long.class.equals(paramTypes[i])) { params.add(Long.valueOf(paramString)); } else if (TimeUnit.class.equals(paramTypes[i])) { params.add(TimeUnit.valueOf(paramString.toUpperCase())); } else if (WaitStrategy.class.equals(paramTypes[i])) { params.add(createWaitStrategyFromString(paramString)); } else { throw new IllegalArgumentException("Unknown paramType " + paramTypes[i]); } } if (params.size() != paramTypes.length) { throw new IllegalArgumentException(String.format("%d parameters must be provided for waitStrategyType %s. %d were provided.", paramTypes.length, waitStrategyType, params.size())); } return params; } /** * Extracts the parameters string (i.e. the part between the curly braces) from the waitStrategyType string. * * @throws IllegalArgumentException if no param string was found, or it is invalid. */ private static String extractParamsString(String waitStrategyType, Class<?>... paramTypes) { int startIndex = waitStrategyType.indexOf(PARAM_START_CHAR); if (startIndex == -1) { throw new IllegalArgumentException( String.format("%d parameters must be provided for waitStrategyType %s." + " None were provided." + " To provide parameters, add a comma separated value list within curly braces ({}) to the end of the waitStrategyType string.", paramTypes.length, waitStrategyType)); } int endIndex = waitStrategyType.lastIndexOf(PARAM_END_CHAR); if (endIndex == -1) { throw new IllegalArgumentException(String.format("Parameters of %s must end with '}'", waitStrategyType)); } return waitStrategyType.substring(startIndex + 1, endIndex); } /** * Finds the end character index of the parameter within the paramsString that starts at startIndex. * * Takes into account nesting of parameters. * * @param paramsString * @param startIndex index within paramsString to start looking * @return index at which the parameter string ends (e.g. the next comma, or paramsString length if no comma found) * * @throws IllegalArgumentException if the parameter is not well formed */ private static int findParamEndIndex(String paramsString, int startIndex) { int nestLevel = 0; for (int c = startIndex; c < paramsString.length(); c++) { char character = paramsString.charAt(c); if (character == PARAM_START_CHAR) { nestLevel++; } else if (character == PARAM_END_CHAR) { nestLevel--; if (nestLevel < 0) { throw new IllegalArgumentException(String.format("Unbalanced '}' at character position %d in %s", c, paramsString)); } } else if (character == PARAM_SEPARATOR_CHAR && nestLevel == 0) { return c; } } if (nestLevel != 0) { throw new IllegalArgumentException(String.format("Unbalanced '{' in %s", paramsString)); } return paramsString.length(); } }