/* * Copyright 2014 the original author or authors. * * 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 org.springframework.xd.dirt.zookeeper; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.Collections; import java.util.EnumSet; import java.util.Map; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.slf4j.Logger; import org.springframework.core.convert.converter.Converter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; /** * Utility methods for ZooKeeper. * * @author David Turanski * @author Patrick Peralta * @author Ilayaperumal Gopinathan */ public abstract class ZooKeeperUtils { /** * Utility to convert byte arrays to maps of strings. */ private static final MapBytesUtility mapBytesUtility = new MapBytesUtility(); /** * {@link Converter} from {@link ChildData} to the leaf path name string. */ public static final StripPathConverter stripPathConverter = new StripPathConverter(); /** * Converter from {@link ChildData} to leaf name string. */ public static class StripPathConverter implements Converter<ChildData, String> { /** * {@inheritDoc} */ @Override public String convert(ChildData source) { return Paths.stripPath(source.getPath()); } } /** * Convert a map of string key/value pairs to a JSON string in a byte array. * * @param map map to convert * * @return byte array */ public static byte[] mapToBytes(Map<String, String> map) { return mapBytesUtility.toByteArray(map); } /** * Convert a byte array containing a JSON string to a map of key/value pairs. * * @param bytes byte array containing the key/value pair strings * * @return a new map instance containing the key/value pairs */ public static Map<String, String> bytesToMap(byte[] bytes) { return mapBytesUtility.toMap(bytes); } /** * Utility method to wrap a Throwable in a {@link ZooKeeperAccessException}. * @param t the throwable */ public static RuntimeException wrapThrowable(Throwable t) { return wrapThrowable(t, null); } /** * Utility method to wrap a Throwable in a {@link ZooKeeperAccessException}. * @param t the throwable * @param message use this message if not null */ public static RuntimeException wrapThrowable(Throwable t, String message) { if (message != null) { return new ZooKeeperAccessException(message, t); } if (t instanceof RuntimeException) { return (RuntimeException) t; } else { return new ZooKeeperAccessException(t.getMessage(), t); } } /** * Throw a wrapped exception ignoring specific Exception types, if any. * @param t the throwable * @param ignored a varargs list of ignored exception types */ @SuppressWarnings({"unchecked", "rawtypes"}) public static void wrapAndThrowIgnoring(Throwable t, Class... ignored) { if (ignored != null) { for (Class<? extends Exception> e : ignored) { if (e.isAssignableFrom(t.getClass())) { return; } } } throw wrapThrowable(t); } /** * Utility method to log {@link org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent events}. * * @param logger logger to write to * @param event event to log */ public static void logCacheEvent(Logger logger, PathChildrenCacheEvent event) { ChildData data = event.getData(); StringBuilder builder = new StringBuilder(); builder.append("Path cache event: "); if (data != null && data.getPath() != null) { builder.append("path=").append(data.getPath()).append(", "); } builder.append("type=").append(event.getType()); if (EnumSet.of(PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED, PathChildrenCacheEvent.Type.CONNECTION_LOST).contains(event.getType())) { logger.warn(builder.toString()); } else { logger.info(builder.toString()); } if (data != null && logger.isTraceEnabled()) { String content; byte[] bytes = data.getData(); if (bytes == null || bytes.length == 0) { content = "empty"; } else { try { content = new String(data.getData(), "UTF-8"); } catch (UnsupportedEncodingException e) { content = "Could not convert content to UTF-8: " + e.toString(); } } logger.trace("Data for path {}: {}", data.getPath(), content); } } /** * Return the full stack trace for a Throwable. * * @param t throwable for which to obtain the full stack trace * @return string containing the full stack trace */ public static String getStackTrace(Throwable t) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); t.printStackTrace(printWriter); return stringWriter.toString(); } /** * Utility to convert {@link Map string key/value pairs} to/from byte arrays containing JSON strings. By default the * JSON library encodes to UTF-8. * * @author Patrick Peralta */ private static class MapBytesUtility { /** * Serializer from map to JSON string in a byte array. */ private final ObjectWriter writer; /** * Deserializer from JSON string in a byte array to a map. */ private final ObjectReader reader; /** * Construct a MapBytesUtility. */ private MapBytesUtility() { ObjectMapper mapper = new ObjectMapper(); writer = mapper.writer(); reader = mapper.reader(Map.class); } /** * Convert a map of string key/value pairs to a JSON string in a byte array. * * @param map map to convert * * @return byte array */ private byte[] toByteArray(Map<String, String> map) { try { return writer.writeValueAsBytes(map); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } /** * Convert a byte array containing a JSON string to a map of key/value pairs. * * @param bytes byte array containing the key/value pair strings * * @return a new map instance containing the key/value pairs */ private Map<String, String> toMap(byte[] bytes) { if (bytes == null || bytes.length == 0) { return Collections.emptyMap(); } try { return reader.readValue(bytes); } catch (Exception e) { String contents; try { contents = new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException uue) { contents = "Could not read content due to " + uue; } throw new RuntimeException("Error parsing JSON string: " + contents, e); } } } }