Java tutorial
/* * This file is part of dropwizard-curator. * * dropwizard-curator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * dropwizard-curator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with dropwizard-curator. If not, see <http://www.gnu.org/licenses/>. */ package com.pandich.dropwizard.curator.refresh; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Maps; import com.pandich.dropwizard.curator.ClassMember; import com.pandich.dropwizard.curator.CuratorInject; import com.pandich.dropwizard.curator.CuratorRoot; import com.pandich.dropwizard.curator.CuratorRootConfiguration; import com.pandich.dropwizard.curator.NodeValue; import com.pandich.dropwizard.curator.PropertySources; import com.pandich.dropwizard.curator.PropertySources.PropertySource; import com.pandich.dropwizard.curator.mapper.CuratorMapper; import io.dropwizard.jackson.Jackson; import org.apache.commons.lang3.CharUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorListener; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Member; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Map; import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE; import static com.google.common.base.CaseFormat.UPPER_CAMEL; import static com.pandich.dropwizard.curator.PropertySources.PropertySource.DROPWIZARD; import static com.pandich.dropwizard.curator.PropertySources.PropertySource.ZOOKEEPER; import static org.apache.commons.lang3.ArrayUtils.subarray; import static org.apache.commons.lang3.ClassUtils.isAssignable; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.join; import static org.apache.commons.lang3.StringUtils.lowerCase; import static org.apache.commons.lang3.StringUtils.split; import static org.apache.commons.lang3.StringUtils.startsWith; import static org.apache.commons.lang3.StringUtils.strip; abstract class Refresher<A extends AccessibleObject & Member> implements CuratorListener { private static final Logger log = LoggerFactory.getLogger(Refresher.class); private static final String PATH_DELIMITER = "/"; private static final ObjectMapper objectMapper = Jackson.newObjectMapper(); private static String getNodePath(final Class<?> clazz, final String name, final CuratorInject curatorInject) { final String path; if (startsWith(curatorInject.value(), PATH_DELIMITER)) { path = curatorInject.value(); } else { final String rootPath; final CuratorRoot curatorRoot = clazz.getAnnotation(CuratorRoot.class); if (curatorRoot != null && !isBlank(curatorRoot.value())) { rootPath = curatorRoot.value(); } else { final String underscoreClassName = UPPER_CAMEL.to(LOWER_UNDERSCORE, clazz.getSimpleName()); final String[] pieces = split(underscoreClassName, "_"); rootPath = join(subarray(pieces, 0, pieces.length - 1)); } final String nodeSubPath = defaultIfBlank(curatorInject.value(), name); final String normalizedRootPath = strip(rootPath, PATH_DELIMITER); path = lowerCase(PATH_DELIMITER + normalizedRootPath + PATH_DELIMITER + nodeSubPath); } log.debug("path: {}.{}={}", new Object[] { clazz.getSimpleName(), name, path }); return path; } protected final Map<String, ClassMember<? extends Member>> pathElements = Maps.newHashMap(); protected final Map<Class<?>, CuratorMapper<?>> mappers; protected final PropertySources propertySources = new PropertySources(); protected final CuratorFramework client; protected final CuratorRootConfiguration configuration; public Refresher(final Map<Class<?>, CuratorMapper<?>> mappers, final CuratorFramework client, final CuratorRootConfiguration configuration) { this.mappers = mappers; this.client = client; this.configuration = configuration; } public final void refresh(final Class<? extends CuratorRootConfiguration> configurationClass, final A element) throws Exception { final String name = element.getName(); log.debug("element: {}.{}", configurationClass.getSimpleName(), name); final CuratorInject curatorInject = element.getAnnotation(CuratorInject.class); try { final NodeValue nodeValue = getNodeValue(configurationClass, name, curatorInject); if (nodeValue.isBlank()) { return; } final Object value = getValue(element, nodeValue); write(element, value); log.debug("element: {}.{}={}", new Object[] { configurationClass.getSimpleName(), name, value }); register(configurationClass, element, ZOOKEEPER); final String path = nodeValue.getPath(); final ClassMember<A> member = new ClassMember<>(configurationClass, element); this.pathElements.put(path, member); } catch (KeeperException.NoNodeException ignore) { register(configurationClass, element, DROPWIZARD); } } @Override public void eventReceived(final CuratorFramework eventClient, final CuratorEvent event) throws Exception { if (event == null) { return; } log.debug("event={}", event); log.info("event {} on {} ", event.getType().name(), event.getPath()); final ClassMember<? extends Member> member = this.pathElements.get(event.getPath()); if (member != null && event.getPath() != null) { refresh(member.getConfigurationClass(), castMember(member.getMember())); } } protected final Object getValue(final A element, final NodeValue nodeValue) { final String rawValue = nodeValue.getValue(); final Class<?> clazz = getType(element); final CuratorMapper<?> mapper = this.mappers.get(clazz); if (mapper != null) { return mapper.readValueFroMString(rawValue); } if (isAssignable(clazz, String.class)) { return rawValue; } if (isAssignable(clazz, Boolean.class)) { return Boolean.valueOf(rawValue); } if (isAssignable(clazz, Character.class)) { return CharUtils.toChar(rawValue); } if (isAssignable(clazz, Byte.class)) { return Byte.valueOf(rawValue); } if (isAssignable(clazz, Short.class)) { return Short.valueOf(rawValue); } if (isAssignable(clazz, Integer.class)) { return Integer.valueOf(rawValue); } if (isAssignable(clazz, Long.class)) { return Long.valueOf(rawValue); } if (isAssignable(clazz, Float.class)) { return Float.valueOf(rawValue); } if (isAssignable(clazz, Double.class)) { return Double.valueOf(rawValue); } if (isAssignable(clazz, BigInteger.class)) { return new BigInteger(rawValue); } if (isAssignable(clazz, BigDecimal.class)) { return new BigDecimal(rawValue); } try { return objectMapper.readValue(rawValue, clazz); } catch (IOException e) { throw new RuntimeException("type '" + clazz.getName() + "' is not supported", e); } } protected final A castMember(final Object o) { if (o == null) { return null; } return doCastMember(o); } private NodeValue getNodeValue(final Class<?> clazz, final String name, final CuratorInject curatorInject) throws Exception { final String nodePath = getNodePath(clazz, name, curatorInject); final String nodeValue; if (curatorInject.refresh()) { nodeValue = new String(this.client.getData().watched().forPath(nodePath)); } else { nodeValue = new String(this.client.getData().forPath(nodePath)); } return new NodeValue(nodePath, nodeValue); } private void write(A element, Object value) throws ReflectiveOperationException { log.debug("write: {} <- {}", element.getName(), value); doWrite(element, value); } protected abstract A doCastMember(final Object o); protected abstract Class<?> getType(A element); protected abstract void doWrite(A element, Object value) throws ReflectiveOperationException; protected abstract void register(Class<? extends CuratorRootConfiguration> configurationClass, A element, PropertySource source); }