Java tutorial
/* * Copyright 2013-2016 EMC Corporation. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * or in the "license" file accompanying this file. This file 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 com.emc.ecs.sync.config; import com.emc.ecs.sync.config.annotation.FilterConfig; import com.emc.ecs.sync.config.annotation.Option; import com.emc.ecs.sync.config.annotation.StorageConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.AnnotationTypeFilter; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; public final class ConfigUtil { public static final String XML_NAMESPACE = "http://www.emc.com/ecs/sync/model"; private static final Logger log = LoggerFactory.getLogger(ConfigUtil.class); private static final Map<Class<?>, ConfigWrapper<?>> wrapperCache = new HashMap<>(); private static ClassPathScanningCandidateComponentProvider storageScanner; private static ClassPathScanningCandidateComponentProvider filterScanner; static { storageScanner = new ClassPathScanningCandidateComponentProvider(false); storageScanner.addIncludeFilter(new AnnotationTypeFilter(StorageConfig.class)); filterScanner = new ClassPathScanningCandidateComponentProvider(false); filterScanner.addIncludeFilter(new AnnotationTypeFilter(FilterConfig.class)); } @SuppressWarnings("unchecked") public static synchronized <C> ConfigWrapper<C> wrapperFor(Class<C> targetClass) { ConfigWrapper<C> configWrapper = (ConfigWrapper<C>) wrapperCache.get(targetClass); if (configWrapper == null) { configWrapper = new ConfigWrapper<>(targetClass); wrapperCache.put(targetClass, configWrapper); } return configWrapper; } @SuppressWarnings("unchecked") public static <C> void parseUri(C configObject, String uri) { wrapperFor((Class<C>) configObject.getClass()).parseUri(configObject, uri); } @SuppressWarnings("unchecked") public static <C> String generateUri(C configObject) { return wrapperFor((Class<C>) configObject.getClass()).generateUri(configObject); } public static ConfigWrapper<?> storageConfigWrapperFor(String uri) { for (ConfigWrapper<?> wrapper : allStorageConfigWrappers()) { if (uri.startsWith(wrapper.getUriPrefix())) return wrapper; } throw new IllegalArgumentException("No storage config defined for URI " + uri); } public static ConfigWrapper<?> filterConfigWrapperFor(String cliName) { for (ConfigWrapper<?> wrapper : allFilterConfigWrappers()) { if (cliName.equals(wrapper.getCliName())) return wrapper; } throw new IllegalArgumentException("No filter config named " + cliName); } public static Iterable<ConfigWrapper<?>> allStorageConfigWrappers() { return new Iterable<ConfigWrapper<?>>() { @Override public Iterator<ConfigWrapper<?>> iterator() { return new WrapperIterator(storageScanner.findCandidateComponents("com.emc.ecs.sync").iterator()); } }; } public static Iterable<ConfigWrapper<?>> allFilterConfigWrappers() { return new Iterable<ConfigWrapper<?>>() { @Override public Iterator<ConfigWrapper<?>> iterator() { return new WrapperIterator(filterScanner.findCandidateComponents("com.emc.ecs.sync").iterator()); } }; } public static String hyphenate(String name) { StringBuilder hyphenated = new StringBuilder(); for (char c : name.toCharArray()) { if (Character.isUpperCase(c) && hyphenated.length() > 0) hyphenated.append('-'); hyphenated.append(Character.toLowerCase(c)); } return hyphenated.toString(); } public static String labelize(String name) { StringBuilder label = new StringBuilder(); for (char c : name.toCharArray()) { if (Character.isUpperCase(c) && label.length() > 0) label.append(' '); label.append(label.length() == 0 ? Character.toUpperCase(c) : c); } return label.toString(); } @SuppressWarnings("unchecked") public static <C> String summarize(C configObject) { return wrapperFor((Class<C>) configObject.getClass()).summarize(configObject); } public static void validate(Object configObject) { try { ConfigWrapper<?> wrapper = wrapperFor(configObject.getClass()); for (String property : wrapper.propertyNames()) { ConfigPropertyWrapper propertyWrapper = wrapper.getPropertyWrapper(property); Object value = propertyWrapper.getDescriptor().getReadMethod().invoke(configObject); // check required if (propertyWrapper.isRequired() && value == null) throw new ConfigurationException( wrapper.getTargetClass().getSimpleName() + "." + property + " is required"); } } catch (InvocationTargetException | IllegalAccessException e) { throw new RuntimeException(e); } } /** * convert an annotated getter into a commons-cli Option */ public static org.apache.commons.cli.Option cliOptionFromAnnotation(PropertyDescriptor descriptor, Option _option, String prefix) { org.apache.commons.cli.Option option = new org.apache.commons.cli.Option(null, _option.description()); // required if (_option.required()) option.setRequired(true); // long name String longName; if (_option.cliName().length() > 0) { longName = _option.cliName(); } else { longName = hyphenate(descriptor.getName()); if ((Boolean.class == descriptor.getPropertyType() || "boolean".equals(descriptor.getPropertyType().getName())) && _option.cliInverted()) longName = "no-" + longName; } if (prefix != null) longName = prefix + longName; option.setLongOpt(longName); // parameter[s] if (descriptor.getPropertyType().isArray()) { option.setArgs(org.apache.commons.cli.Option.UNLIMITED_VALUES); } else if (Boolean.class != descriptor.getPropertyType() && !"boolean".equals(descriptor.getPropertyType().getName())) { // non-booleans *must* have an argument option.setArgs(1); } if (option.hasArg()) { if (_option.valueHint().length() > 0) option.setArgName(_option.valueHint()); else option.setArgName(option.getLongOpt()); } return option; } public static String join(String[] parts) { if (parts == null || parts.length == 0) return null; StringBuilder joined = new StringBuilder(); for (int i = 0; i < parts.length; i++) { joined.append(parts[i]); if (i < parts.length - 1) joined.append(","); } return joined.toString(); } private ConfigUtil() { } private static class WrapperIterator implements Iterator<ConfigWrapper<?>> { private Iterator<BeanDefinition> definitionIterator; private ConfigWrapper<?> nextElement; public WrapperIterator(Iterator<BeanDefinition> definitionIterator) { this.definitionIterator = definitionIterator; findNext(); } @Override public boolean hasNext() { return nextElement != null; } @Override public ConfigWrapper<?> next() { if (nextElement == null) throw new NoSuchElementException(); ConfigWrapper<?> element = nextElement; findNext(); return element; } @Override public void remove() { throw new UnsupportedOperationException("this is a read-only iterator"); } @SuppressWarnings("unchecked") private void findNext() { nextElement = null; while (definitionIterator.hasNext() && nextElement == null) { BeanDefinition beanDef = definitionIterator.next(); try { nextElement = wrapperFor(Class.forName(beanDef.getBeanClassName())); } catch (ClassNotFoundException e) { log.warn("could not load plugin config " + beanDef.getBeanClassName(), e); } catch (UnsupportedClassVersionError e) { log.warn("the plugin for " + beanDef.getBeanClassName() + " is not supported in this version of java", e); } } } } }