http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/FactoryFinder.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/FactoryFinder.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/FactoryFinder.java new file mode 100644 index 0000000..ffd0726 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/FactoryFinder.java @@ -0,0 +1,166 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +public class FactoryFinder +{ + /** + * The strategy that the FactoryFinder uses to find load and instantiate Objects + * can be changed out by calling the + * {@link org.apache.activemq6.utils.FactoryFinder#setObjectFactory(org.hornetq.utils.FactoryFinder.ObjectFactory)} + * method with a custom implementation of ObjectFactory. + * <p/> + * The default ObjectFactory is typically changed out when running in a specialized container + * environment where service discovery needs to be done via the container system. For example, + * in an OSGi scenario. + */ + public interface ObjectFactory + { + /** + * @param path the full service path + * @return + */ + Object create(String path) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException; + + } + + /** + * The default implementation of Object factory which works well in standalone applications. + */ + protected static class StandaloneObjectFactory implements ObjectFactory + { + final ConcurrentHashMap<String, Class> classMap = new ConcurrentHashMap<String, Class>(); + + public Object create(final String path) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException + { + Class clazz = classMap.get(path); + if (clazz == null) + { + clazz = loadClass(loadProperties(path)); + classMap.put(path, clazz); + } + return clazz.newInstance(); + } + + static Class loadClass(Properties properties) throws ClassNotFoundException, IOException + { + + String className = properties.getProperty("class"); + if (className == null) + { + throw new IOException("Expected property is missing: class"); + } + Class clazz = null; + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader != null) + { + try + { + clazz = loader.loadClass(className); + } + catch (ClassNotFoundException e) + { + // ignore + } + } + if (clazz == null) + { + clazz = FactoryFinder.class.getClassLoader().loadClass(className); + } + + return clazz; + } + + public Properties loadProperties(String uri) throws IOException + { + // lets try the thread context class loader first + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) + { + classLoader = StandaloneObjectFactory.class.getClassLoader(); + } + InputStream in = classLoader.getResourceAsStream(uri); + if (in == null) + { + in = FactoryFinder.class.getClassLoader().getResourceAsStream(uri); + if (in == null) + { + throw new IOException("Could not find factory class for resource: " + uri); + } + } + + // lets load the file + BufferedInputStream reader = null; + try + { + reader = new BufferedInputStream(in); + Properties properties = new Properties(); + properties.load(reader); + return properties; + } + finally + { + try + { + reader.close(); + } + catch (Exception e) + { + } + } + } + } + + // ================================================================ + // Class methods and properties + // ================================================================ + private static ObjectFactory objectFactory = new StandaloneObjectFactory(); + + public static ObjectFactory getObjectFactory() + { + return objectFactory; + } + + public static void setObjectFactory(ObjectFactory objectFactory) + { + FactoryFinder.objectFactory = objectFactory; + } + + // ================================================================ + // Instance methods and properties + // ================================================================ + private final String path; + + public FactoryFinder(String path) + { + this.path = path; + } + + /** + * Creates a new instance of the given key + * + * @param key is the key to add to the path to find a text file containing + * the factory name + * @return a newly created instance + */ + public Object newInstance(String key) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException + { + return objectFactory.create(path + key); + } +}
http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQThreadFactory.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQThreadFactory.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQThreadFactory.java new file mode 100644 index 0000000..68341d3 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQThreadFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * A HornetQThreadFactory + * + * @author <a href="mailto:[email protected]">Tim Fox</a> + * + */ +public final class HornetQThreadFactory implements ThreadFactory +{ + private final ThreadGroup group; + + private final AtomicInteger threadCount = new AtomicInteger(0); + + private final int threadPriority; + + private final boolean daemon; + + private final ClassLoader tccl; + + public HornetQThreadFactory(final String groupName, final boolean daemon, final ClassLoader tccl) + { + group = new ThreadGroup(groupName + "-" + System.identityHashCode(this)); + + this.threadPriority = Thread.NORM_PRIORITY; + + this.tccl = tccl; + + this.daemon = daemon; + } + + public Thread newThread(final Runnable command) + { + // always create a thread in a privileged block. + return AccessController.doPrivileged(new PrivilegedAction<Thread>() + { + @Override + public Thread run() + { + final Thread t = new Thread(group, command, "Thread-" + threadCount.getAndIncrement() + " (" + group.getName() + ")"); + t.setDaemon(daemon); + t.setPriority(threadPriority); + t.setContextClassLoader(tccl); + + return t; + } + }); + } + +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilBundle.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilBundle.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilBundle.java new file mode 100644 index 0000000..42fc4c6 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilBundle.java @@ -0,0 +1,48 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + + +import org.apache.activemq6.api.core.HornetQIllegalStateException; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageBundle; +import org.jboss.logging.Messages; + +/** + * @author <a href="mailto:[email protected]">Andy Taylor</a> + * 3/12/12 + * + * Logger Code 20 + * + * each message id must be 6 digits long starting with 20, the 3rd digit should be 9 + * + * so 209000 to 209999 + */ +@MessageBundle(projectCode = "HQ") +public interface HornetQUtilBundle +{ + HornetQUtilBundle BUNDLE = Messages.getBundle(HornetQUtilBundle.class); + + @Message(id = 209000, value = "invalid property: {0}" , format = Message.Format.MESSAGE_FORMAT) + HornetQIllegalStateException invalidProperty(String part); + + @Message(id = 209001, value = "Invalid type: {0}", format = Message.Format.MESSAGE_FORMAT) + IllegalStateException invalidType(Byte type); + + @Message(id = 209002, value = "the specified string is too long ({0})", format = Message.Format.MESSAGE_FORMAT) + IllegalStateException stringTooLong(Integer length); + + @Message(id = 209003, value = "Error instantiating codec {0}", format = Message.Format.MESSAGE_FORMAT) + IllegalArgumentException errorCreatingCodec(@Cause Exception e, String codecClassName); +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilLogger.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilLogger.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilLogger.java new file mode 100644 index 0000000..5149f2a --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/HornetQUtilLogger.java @@ -0,0 +1,50 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; + +/** + * @author <a href="mailto:[email protected]">Andy Taylor</a> + * 3/15/12 + * + * Logger Code 20 + * + * each message id must be 6 digits long starting with 20, the 3rd digit donates the level so + * + * INF0 1 + * WARN 2 + * DEBUG 3 + * ERROR 4 + * TRACE 5 + * FATAL 6 + * + * so an INFO message would be 201000 to 201999 + */ +@MessageLogger(projectCode = "HQ") +public interface HornetQUtilLogger extends BasicLogger +{ + /** + * The default logger. + */ + HornetQUtilLogger LOGGER = Logger.getMessageLogger(HornetQUtilLogger.class, HornetQUtilLogger.class.getPackage().getName()); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 202000, value = "Missing privileges to set Thread Context Class Loader on Thread Factory. Using current Thread Context Class Loader", + format = Message.Format.MESSAGE_FORMAT) + void missingPrivsForClassloader(); +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/PasswordMaskingUtil.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/PasswordMaskingUtil.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/PasswordMaskingUtil.java new file mode 100644 index 0000000..c6b5052 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/PasswordMaskingUtil.java @@ -0,0 +1,92 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; + +import org.apache.activemq6.api.core.HornetQException; +import org.apache.activemq6.api.core.HornetQExceptionType; + +/** + * A PasswordMarkingUtil + * + * @author <a href="mailto:[email protected]">Howard Gao</a> + * + * + */ +public class PasswordMaskingUtil +{ + /* + * Loading the codec class. + * + * @param codecDesc This parameter must have the following format: + * + * <full qualified class name>;key=value;key1=value1;... + * + * Where only <full qualified class name> is required. key/value pairs are optional + */ + public static SensitiveDataCodec<String> getCodec(String codecDesc) throws HornetQException + { + SensitiveDataCodec<String> codecInstance = null; + + // semi colons + String[] parts = codecDesc.split(";"); + + if (parts.length < 1) + throw new HornetQException(HornetQExceptionType.ILLEGAL_STATE, "Invalid PasswordCodec value: " + codecDesc); + + final String codecClassName = parts[0]; + + // load class + codecInstance = AccessController.doPrivileged(new PrivilegedAction<SensitiveDataCodec<String>>() + { + public SensitiveDataCodec<String> run() + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try + { + Class<?> clazz = loader.loadClass(codecClassName); + return (SensitiveDataCodec<String>)clazz.newInstance(); + } + catch (Exception e) + { + throw HornetQUtilBundle.BUNDLE.errorCreatingCodec(e, codecClassName); + } + } + }); + + if (parts.length > 1) + { + Map<String, String> props = new HashMap<String, String>(); + + for (int i = 1; i < parts.length; i++) + { + String[] keyVal = parts[i].split("="); + if (keyVal.length != 2) + throw HornetQUtilBundle.BUNDLE.invalidProperty(parts[i]); + props.put(keyVal[0], keyVal[1]); + } + codecInstance.init(props); + } + + return codecInstance; + } + + public static SensitiveDataCodec<String> getDefaultCodec() + { + return new DefaultSensitiveStringCodec(); + } +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounter.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounter.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounter.java new file mode 100644 index 0000000..bbf887f --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounter.java @@ -0,0 +1,24 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +/** + * @author Clebert Suconic + */ + +public interface ReferenceCounter +{ + int increment(); + + int decrement(); +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounterUtil.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounterUtil.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounterUtil.java new file mode 100644 index 0000000..eed841a --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReferenceCounterUtil.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Clebert Suconic + */ + +public class ReferenceCounterUtil implements ReferenceCounter +{ + private final Runnable runnable; + + /** If executor is null the runnable will be called within the same thread, otherwise the executor will be used */ + private final Executor executor; + + private final AtomicInteger uses = new AtomicInteger(0); + + + public ReferenceCounterUtil(Runnable runnable) + { + this(runnable, null); + } + + public ReferenceCounterUtil(Runnable runnable, Executor executor) + { + this.runnable = runnable; + this.executor = executor; + } + + @Override + public int increment() + { + return uses.incrementAndGet(); + } + + @Override + public int decrement() + { + int value = uses.decrementAndGet(); + if (value == 0) + { + if (executor != null) + { + executor.execute(runnable); + } + else + { + runnable.run(); + } + } + + return value; + } +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReusableLatch.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReusableLatch.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReusableLatch.java new file mode 100644 index 0000000..deecfd1 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/ReusableLatch.java @@ -0,0 +1,155 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +/** + * + * <p>This class will use the framework provided to by AbstractQueuedSynchronizer.</p> + * <p>AbstractQueuedSynchronizer is the framework for any sort of concurrent synchronization, such as Semaphores, events, etc, based on AtomicIntegers.</p> + * + * <p>This class works just like CountDownLatch, with the difference you can also increase the counter</p> + * + * <p>It could be used for sync points when one process is feeding the latch while another will wait when everything is done. (e.g. waiting IO completions to finish)</p> + * + * <p>On HornetQ we have the requirement of increment and decrement a counter until the user fires a ready event (commit). At that point we just act as a regular countDown.</p> + * + * <p>Note: This latch is reusable. Once it reaches zero, you can call up again, and reuse it on further waits.</p> + * + * <p>For example: prepareTransaction will wait for the current completions, and further adds will be called on the latch. Later on when commit is called you can reuse the same latch.</p> + * + * @author Clebert Suconic + * */ +public class ReusableLatch +{ + /** + * Look at the doc and examples provided by AbstractQueuedSynchronizer for more information + * @see AbstractQueuedSynchronizer*/ + @SuppressWarnings("serial") + private static class CountSync extends AbstractQueuedSynchronizer + { + public CountSync(int count) + { + setState(count); + } + + public int getCount() + { + return getState(); + } + + public void setCount(final int count) + { + setState(count); + } + + @Override + public int tryAcquireShared(final int numberOfAqcquires) + { + return getState() == 0 ? 1 : -1; + } + + public void add() + { + for (;;) + { + int actualState = getState(); + int newState = actualState + 1; + if (compareAndSetState(actualState, newState)) + { + return; + } + } + } + + @Override + public boolean tryReleaseShared(final int numberOfReleases) + { + for (;;) + { + int actualState = getState(); + if (actualState == 0) + { + return true; + } + + int newState = actualState - numberOfReleases; + + if (newState < 0) + { + newState = 0; + } + + if (compareAndSetState(actualState, newState)) + { + return newState == 0; + } + } + } + } + + private final CountSync control; + + public ReusableLatch() + { + this(0); + } + + public ReusableLatch(final int count) + { + control = new CountSync(count); + } + + public int getCount() + { + return control.getCount(); + } + + public void setCount(final int count) + { + control.setCount(count); + } + + public void countUp() + { + control.add(); + } + + public void countDown() + { + control.releaseShared(1); + } + + + public void countDown(final int count) + { + control.releaseShared(count); + } + + public void await() throws InterruptedException + { + control.acquireSharedInterruptibly(1); + } + + public boolean await(final long milliseconds) throws InterruptedException + { + return control.tryAcquireSharedNanos(1, TimeUnit.MILLISECONDS.toNanos(milliseconds)); + } + + public boolean await(final long timeWait, TimeUnit timeUnit) throws InterruptedException + { + return control.tryAcquireSharedNanos(1, timeUnit.toNanos(timeWait)); + } +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/SensitiveDataCodec.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/SensitiveDataCodec.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/SensitiveDataCodec.java new file mode 100644 index 0000000..0bf6c52 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/SensitiveDataCodec.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.util.Map; + +/** + * A SensitiveDataCodec + * + * This interface is used for implementing a value decoder. + * + * It takes in a mask value and decode it. + * + * @author <a href="mailto:[email protected]">Howard Gao</a> + * + * + */ +public interface SensitiveDataCodec<T> +{ + T decode(Object mask) throws Exception; + + void init(Map<String, String> params); +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/TypedProperties.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/TypedProperties.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/TypedProperties.java new file mode 100644 index 0000000..6e5d54d --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/TypedProperties.java @@ -0,0 +1,1212 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.activemq6.api.core.HornetQBuffer; +import org.apache.activemq6.api.core.HornetQPropertyConversionException; +import org.apache.activemq6.api.core.SimpleString; + +import static org.apache.activemq6.utils.DataConstants.BOOLEAN; +import static org.apache.activemq6.utils.DataConstants.BYTE; +import static org.apache.activemq6.utils.DataConstants.BYTES; +import static org.apache.activemq6.utils.DataConstants.CHAR; +import static org.apache.activemq6.utils.DataConstants.DOUBLE; +import static org.apache.activemq6.utils.DataConstants.FLOAT; +import static org.apache.activemq6.utils.DataConstants.INT; +import static org.apache.activemq6.utils.DataConstants.LONG; +import static org.apache.activemq6.utils.DataConstants.NULL; +import static org.apache.activemq6.utils.DataConstants.SHORT; +import static org.apache.activemq6.utils.DataConstants.STRING; + +/** + * Property Value Conversion. + * <p> + * This implementation follows section 3.5.4 of the <i>Java Message Service</i> specification + * (Version 1.1 April 12, 2002). + * <p> + * TODO - should have typed property getters and do conversions herein + * + * @author <a href="mailto:[email protected]">Tim Fox</a> + * @author <a href="mailto:[email protected]">Clebert Suconic</a> + */ +public final class TypedProperties +{ + + private static final SimpleString HQ_PROPNAME = new SimpleString("_HQ_"); + + private Map<SimpleString, PropertyValue> properties; + + private volatile int size; + + private boolean internalProperties; + + public TypedProperties() + { + } + + public int getMemoryOffset() + { + // The estimate is basically the encode size + 2 object references for each entry in the map + // Note we don't include the attributes or anything else since they already included in the memory estimate + // of the ServerMessage + + return properties == null ? 0 : size + 2 * DataConstants.SIZE_INT * properties.size(); + } + + public TypedProperties(final TypedProperties other) + { + properties = other.properties == null ? null : new HashMap<SimpleString, PropertyValue>(other.properties); + size = other.size; + } + + public boolean hasInternalProperties() + { + return internalProperties; + } + + public void putBooleanProperty(final SimpleString key, final boolean value) + { + checkCreateProperties(); + doPutValue(key, new BooleanValue(value)); + } + + public void putByteProperty(final SimpleString key, final byte value) + { + checkCreateProperties(); + doPutValue(key, new ByteValue(value)); + } + + public void putBytesProperty(final SimpleString key, final byte[] value) + { + checkCreateProperties(); + doPutValue(key, value == null ? new NullValue() : new BytesValue(value)); + } + + public void putShortProperty(final SimpleString key, final short value) + { + checkCreateProperties(); + doPutValue(key, new ShortValue(value)); + } + + public void putIntProperty(final SimpleString key, final int value) + { + checkCreateProperties(); + doPutValue(key, new IntValue(value)); + } + + public void putLongProperty(final SimpleString key, final long value) + { + checkCreateProperties(); + doPutValue(key, new LongValue(value)); + } + + public void putFloatProperty(final SimpleString key, final float value) + { + checkCreateProperties(); + doPutValue(key, new FloatValue(value)); + } + + public void putDoubleProperty(final SimpleString key, final double value) + { + checkCreateProperties(); + doPutValue(key, new DoubleValue(value)); + } + + public void putSimpleStringProperty(final SimpleString key, final SimpleString value) + { + checkCreateProperties(); + doPutValue(key, value == null ? new NullValue() : new StringValue(value)); + } + + public void putNullValue(final SimpleString key) + { + checkCreateProperties(); + doPutValue(key, new NullValue()); + } + + public void putCharProperty(final SimpleString key, final char value) + { + checkCreateProperties(); + doPutValue(key, new CharValue(value)); + } + + public void putTypedProperties(final TypedProperties otherProps) + { + if (otherProps == null || otherProps.properties == null) + { + return; + } + + checkCreateProperties(); + Set<Entry<SimpleString, PropertyValue>> otherEntries = otherProps.properties.entrySet(); + for (Entry<SimpleString, PropertyValue> otherEntry : otherEntries) + { + doPutValue(otherEntry.getKey(), otherEntry.getValue()); + } + } + + public Object getProperty(final SimpleString key) + { + return doGetProperty(key); + } + + public Boolean getBooleanProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return Boolean.valueOf(null); + } + else if (value instanceof Boolean) + { + return (Boolean) value; + } + else if (value instanceof SimpleString) + { + return Boolean.valueOf(((SimpleString) value).toString()); + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public Byte getByteProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return Byte.valueOf(null); + } + else if (value instanceof Byte) + { + return (Byte) value; + } + else if (value instanceof SimpleString) + { + return Byte.parseByte(((SimpleString) value).toString()); + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public Character getCharProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + throw new NullPointerException("Invalid conversion"); + } + + if (value instanceof Character) + { + return ((Character) value); + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public byte[] getBytesProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return null; + } + else if (value instanceof byte[]) + { + return (byte[]) value; + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public Double getDoubleProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return Double.valueOf(null); + } + else if (value instanceof Float) + { + return ((Float) value).doubleValue(); + } + else if (value instanceof Double) + { + return (Double) value; + } + else if (value instanceof SimpleString) + { + return Double.parseDouble(((SimpleString) value).toString()); + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public Integer getIntProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return Integer.valueOf(null); + } + else if (value instanceof Integer) + { + return (Integer) value; + } + else if (value instanceof Byte) + { + return ((Byte) value).intValue(); + } + else if (value instanceof Short) + { + return ((Short) value).intValue(); + } + else if (value instanceof SimpleString) + { + return Integer.parseInt(((SimpleString) value).toString()); + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public Long getLongProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return Long.valueOf(null); + } + else if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof SimpleString) + { + return Long.parseLong(((SimpleString) value).toString()); + } + else + { + throw new HornetQPropertyConversionException("Invalid conversion"); + } + } + + public Short getShortProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + { + return Short.valueOf(null); + } + else if (value instanceof Byte) + { + return ((Byte) value).shortValue(); + } + else if (value instanceof Short) + { + return (Short) value; + } + else if (value instanceof SimpleString) + { + return Short.parseShort(((SimpleString) value).toString()); + } + else + { + throw new HornetQPropertyConversionException("Invalid Conversion."); + } + } + + public Float getFloatProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + if (value == null) + return Float.valueOf(null); + if (value instanceof Float) + { + return ((Float) value); + } + if (value instanceof SimpleString) + { + return Float.parseFloat(((SimpleString) value).toString()); + } + throw new HornetQPropertyConversionException("Invalid conversion: " + key); + } + + public SimpleString getSimpleStringProperty(final SimpleString key) throws HornetQPropertyConversionException + { + Object value = doGetProperty(key); + + if (value == null) + { + return null; + } + + if (value instanceof SimpleString) + { + return (SimpleString) value; + } + else if (value instanceof Boolean) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Character) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Byte) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Short) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Integer) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Long) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Float) + { + return new SimpleString(value.toString()); + } + else if (value instanceof Double) + { + return new SimpleString(value.toString()); + } + throw new HornetQPropertyConversionException("Invalid conversion"); + } + + public Object removeProperty(final SimpleString key) + { + return doRemoveProperty(key); + } + + public boolean containsProperty(final SimpleString key) + { + if (size == 0) + { + return false; + + } + else + { + return properties.containsKey(key); + } + } + + public Set<SimpleString> getPropertyNames() + { + if (size == 0) + { + return Collections.emptySet(); + } + else + { + return properties.keySet(); + } + } + + public synchronized void decode(final HornetQBuffer buffer) + { + byte b = buffer.readByte(); + + if (b == DataConstants.NULL) + { + properties = null; + } + else + { + int numHeaders = buffer.readInt(); + + properties = new HashMap<SimpleString, PropertyValue>(numHeaders); + size = 0; + + for (int i = 0; i < numHeaders; i++) + { + int len = buffer.readInt(); + byte[] data = new byte[len]; + buffer.readBytes(data); + SimpleString key = new SimpleString(data); + + byte type = buffer.readByte(); + + PropertyValue val; + + switch (type) + { + case NULL: + { + val = new NullValue(); + doPutValue(key, val); + break; + } + case CHAR: + { + val = new CharValue(buffer); + doPutValue(key, val); + break; + } + case BOOLEAN: + { + val = new BooleanValue(buffer); + doPutValue(key, val); + break; + } + case BYTE: + { + val = new ByteValue(buffer); + doPutValue(key, val); + break; + } + case BYTES: + { + val = new BytesValue(buffer); + doPutValue(key, val); + break; + } + case SHORT: + { + val = new ShortValue(buffer); + doPutValue(key, val); + break; + } + case INT: + { + val = new IntValue(buffer); + doPutValue(key, val); + break; + } + case LONG: + { + val = new LongValue(buffer); + doPutValue(key, val); + break; + } + case FLOAT: + { + val = new FloatValue(buffer); + doPutValue(key, val); + break; + } + case DOUBLE: + { + val = new DoubleValue(buffer); + doPutValue(key, val); + break; + } + case STRING: + { + val = new StringValue(buffer); + doPutValue(key, val); + break; + } + default: + { + throw HornetQUtilBundle.BUNDLE.invalidType(type); + } + } + } + } + } + + public synchronized void encode(final HornetQBuffer buffer) + { + if (properties == null) + { + buffer.writeByte(DataConstants.NULL); + } + else + { + buffer.writeByte(DataConstants.NOT_NULL); + + buffer.writeInt(properties.size()); + + for (Map.Entry<SimpleString, PropertyValue> entry : properties.entrySet()) + { + SimpleString s = entry.getKey(); + byte[] data = s.getData(); + buffer.writeInt(data.length); + buffer.writeBytes(data); + + entry.getValue().write(buffer); + } + } + } + + public int getEncodeSize() + { + if (properties == null) + { + return DataConstants.SIZE_BYTE; + } + else + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + size; + } + } + + public void clear() + { + if (properties != null) + { + properties.clear(); + } + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder("TypedProperties["); + + + if (properties != null) + { + + Iterator<Entry<SimpleString, PropertyValue>> iter = properties.entrySet().iterator(); + + while (iter.hasNext()) + { + Entry<SimpleString, PropertyValue> iterItem = iter.next(); + sb.append(iterItem.getKey() + "="); + + // it seems weird but it's right!! + // The first getValue is from the EntrySet + // The second is to convert the PropertyValue into the actual value + Object theValue = iterItem.getValue().getValue(); + + + if (theValue == null) + { + sb.append("NULL-value"); + } + else if (theValue instanceof byte[]) + { + sb.append("[" + ByteUtil.maxString(ByteUtil.bytesToHex((byte [])theValue, 2), 150) + ")"); + + if (iterItem.getKey().toString().startsWith("_HQ_ROUTE_TO")) + { + sb.append(",bytesAsLongs("); + try + { + ByteBuffer buff = ByteBuffer.wrap((byte[]) theValue); + while (buff.hasRemaining()) + { + long bindingID = buff.getLong(); + sb.append(bindingID); + if (buff.hasRemaining()) + { + sb.append(","); + } + } + } + catch (Throwable e) + { + sb.append("error-converting-longs=" + e.getMessage()); + } + sb.append("]"); + } + } + else + { + sb.append(theValue.toString()); + } + + + if (iter.hasNext()) + { + sb.append(","); + } + } + } + + return sb.append("]").toString(); + } + + // Private ------------------------------------------------------------------------------------ + + private void checkCreateProperties() + { + if (properties == null) + { + properties = new HashMap<SimpleString, PropertyValue>(); + } + } + + private synchronized void doPutValue(final SimpleString key, final PropertyValue value) + { + if (key.startsWith(HQ_PROPNAME)) + { + internalProperties = true; + } + + PropertyValue oldValue = properties.put(key, value); + if (oldValue != null) + { + size += value.encodeSize() - oldValue.encodeSize(); + } + else + { + size += SimpleString.sizeofString(key) + value.encodeSize(); + } + } + + private synchronized Object doRemoveProperty(final SimpleString key) + { + if (properties == null) + { + return null; + } + + PropertyValue val = properties.remove(key); + + if (val == null) + { + return null; + } + else + { + size -= SimpleString.sizeofString(key) + val.encodeSize(); + + return val.getValue(); + } + } + + private synchronized Object doGetProperty(final Object key) + { + if (size == 0) + { + return null; + } + + PropertyValue val = properties.get(key); + + if (val == null) + { + return null; + } + else + { + return val.getValue(); + } + } + + // Inner classes ------------------------------------------------------------------------------ + + private abstract static class PropertyValue + { + abstract Object getValue(); + + abstract void write(HornetQBuffer buffer); + + abstract int encodeSize(); + + @Override + public String toString() + { + return "" + getValue(); + } + } + + private static final class NullValue extends PropertyValue + { + public NullValue() + { + } + + @Override + public Object getValue() + { + return null; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.NULL); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE; + } + + } + + private static final class BooleanValue extends PropertyValue + { + final boolean val; + + public BooleanValue(final boolean val) + { + this.val = val; + } + + public BooleanValue(final HornetQBuffer buffer) + { + val = buffer.readBoolean(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.BOOLEAN); + buffer.writeBoolean(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_BOOLEAN; + } + + } + + private static final class ByteValue extends PropertyValue + { + final byte val; + + public ByteValue(final byte val) + { + this.val = val; + } + + public ByteValue(final HornetQBuffer buffer) + { + val = buffer.readByte(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.BYTE); + buffer.writeByte(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_BYTE; + } + } + + private static final class BytesValue extends PropertyValue + { + final byte[] val; + + public BytesValue(final byte[] val) + { + this.val = val; + } + + public BytesValue(final HornetQBuffer buffer) + { + int len = buffer.readInt(); + val = new byte[len]; + buffer.readBytes(val); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.BYTES); + buffer.writeInt(val.length); + buffer.writeBytes(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + val.length; + } + + } + + private static final class ShortValue extends PropertyValue + { + final short val; + + public ShortValue(final short val) + { + this.val = val; + } + + public ShortValue(final HornetQBuffer buffer) + { + val = buffer.readShort(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.SHORT); + buffer.writeShort(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_SHORT; + } + } + + private static final class IntValue extends PropertyValue + { + final int val; + + public IntValue(final int val) + { + this.val = val; + } + + public IntValue(final HornetQBuffer buffer) + { + val = buffer.readInt(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.INT); + buffer.writeInt(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT; + } + } + + private static final class LongValue extends PropertyValue + { + final long val; + + public LongValue(final long val) + { + this.val = val; + } + + public LongValue(final HornetQBuffer buffer) + { + val = buffer.readLong(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.LONG); + buffer.writeLong(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_LONG; + } + } + + private static final class FloatValue extends PropertyValue + { + final float val; + + public FloatValue(final float val) + { + this.val = val; + } + + public FloatValue(final HornetQBuffer buffer) + { + val = Float.intBitsToFloat(buffer.readInt()); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.FLOAT); + buffer.writeInt(Float.floatToIntBits(val)); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_FLOAT; + } + + } + + private static final class DoubleValue extends PropertyValue + { + final double val; + + public DoubleValue(final double val) + { + this.val = val; + } + + public DoubleValue(final HornetQBuffer buffer) + { + val = Double.longBitsToDouble(buffer.readLong()); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.DOUBLE); + buffer.writeLong(Double.doubleToLongBits(val)); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_DOUBLE; + } + } + + private static final class CharValue extends PropertyValue + { + final char val; + + public CharValue(final char val) + { + this.val = val; + } + + public CharValue(final HornetQBuffer buffer) + { + val = (char) buffer.readShort(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.CHAR); + buffer.writeShort((short) val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + DataConstants.SIZE_CHAR; + } + } + + private static final class StringValue extends PropertyValue + { + final SimpleString val; + + public StringValue(final SimpleString val) + { + this.val = val; + } + + public StringValue(final HornetQBuffer buffer) + { + val = buffer.readSimpleString(); + } + + @Override + public Object getValue() + { + return val; + } + + @Override + public void write(final HornetQBuffer buffer) + { + buffer.writeByte(DataConstants.STRING); + buffer.writeSimpleString(val); + } + + @Override + public int encodeSize() + { + return DataConstants.SIZE_BYTE + SimpleString.sizeofString(val); + } + } + + public boolean isEmpty() + { + return properties.isEmpty(); + } + + public Map<String, Object> getMap() + { + Map<String, Object> m = new HashMap<String, Object>(); + for (Entry<SimpleString, PropertyValue> entry : properties.entrySet()) + { + Object val = entry.getValue().getValue(); + if (val instanceof SimpleString) + { + m.put(entry.getKey().toString(), ((SimpleString) val).toString()); + } + else + { + m.put(entry.getKey().toString(), val); + } + } + return m; + } + + /** + * Helper for {@link MapMessage#setObjectProperty(String, Object)} + * + * @param key + * @param value + * @param properties + */ + public static void setObjectProperty(final SimpleString key, final Object value, final TypedProperties properties) + { + if (value == null) + { + properties.putNullValue(key); + } + else if (value instanceof Boolean) + { + properties.putBooleanProperty(key, (Boolean) value); + } + else if (value instanceof Byte) + { + properties.putByteProperty(key, (Byte) value); + } + else if (value instanceof Character) + { + properties.putCharProperty(key, (Character) value); + } + else if (value instanceof Short) + { + properties.putShortProperty(key, (Short) value); + } + else if (value instanceof Integer) + { + properties.putIntProperty(key, (Integer) value); + } + else if (value instanceof Long) + { + properties.putLongProperty(key, (Long) value); + } + else if (value instanceof Float) + { + properties.putFloatProperty(key, (Float) value); + } + else if (value instanceof Double) + { + properties.putDoubleProperty(key, (Double) value); + } + else if (value instanceof String) + { + properties.putSimpleStringProperty(key, new SimpleString((String) value)); + } + else if (value instanceof SimpleString) + { + properties.putSimpleStringProperty(key, (SimpleString) value); + } + else if (value instanceof byte[]) + { + properties.putBytesProperty(key, (byte[]) value); + } + else + { + throw new HornetQPropertyConversionException(value.getClass() + " is not a valid property type"); + } + } +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/UTF8Util.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/UTF8Util.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/UTF8Util.java new file mode 100644 index 0000000..fb087e5 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/UTF8Util.java @@ -0,0 +1,273 @@ +/* + * Copyright 2005-2014 Red Hat, Inc. + * Red Hat licenses this file to you 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.apache.activemq6.utils; + +import java.lang.ref.SoftReference; + +import org.apache.activemq6.api.core.HornetQBuffer; + +/** + * + * A UTF8Util + * + * This class will write UTFs directly to the ByteOutput (through the MessageBuffer interface) + * + * @author <a href="mailto:[email protected]">Clebert Suconic</a> + * + * Created Feb 20, 2009 1:37:18 PM + * + * + */ +public final class UTF8Util +{ + private UTF8Util() + { + // utility class + } + + private static final boolean isTrace = HornetQUtilLogger.LOGGER.isTraceEnabled(); + + private static final ThreadLocal<SoftReference<StringUtilBuffer>> currenBuffer = + new ThreadLocal<SoftReference<StringUtilBuffer>>(); + + public static void saveUTF(final HornetQBuffer out, final String str) + { + StringUtilBuffer buffer = UTF8Util.getThreadLocalBuffer(); + + if (str.length() > 0xffff) + { + throw HornetQUtilBundle.BUNDLE.stringTooLong(str.length()); + } + + final int len = UTF8Util.calculateUTFSize(str, buffer); + + if (len > 0xffff) + { + throw HornetQUtilBundle.BUNDLE.stringTooLong(len); + } + + out.writeShort((short)len); + + if (len > buffer.byteBuffer.length) + { + buffer.resizeByteBuffer(len); + } + + if (len == (long)str.length()) + { + for (int byteLocation = 0; byteLocation < len; byteLocation++) + { + buffer.byteBuffer[byteLocation] = (byte)buffer.charBuffer[byteLocation]; + } + out.writeBytes(buffer.byteBuffer, 0, len); + } + else + { + if (UTF8Util.isTrace) + { + // This message is too verbose for debug, that's why we are using trace here + HornetQUtilLogger.LOGGER.trace("Saving string with utfSize=" + len + " stringSize=" + str.length()); + } + + int stringLength = str.length(); + + int charCount = 0; + + for (int i = 0; i < stringLength; i++) + { + char charAtPos = buffer.charBuffer[i]; + if (charAtPos >= 1 && charAtPos < 0x7f) + { + buffer.byteBuffer[charCount++] = (byte)charAtPos; + } + else if (charAtPos >= 0x800) + { + buffer.byteBuffer[charCount++] = (byte)(0xE0 | charAtPos >> 12 & 0x0F); + buffer.byteBuffer[charCount++] = (byte)(0x80 | charAtPos >> 6 & 0x3F); + buffer.byteBuffer[charCount++] = (byte)(0x80 | charAtPos >> 0 & 0x3F); + } + else + { + buffer.byteBuffer[charCount++] = (byte)(0xC0 | charAtPos >> 6 & 0x1F); + buffer.byteBuffer[charCount++] = (byte)(0x80 | charAtPos >> 0 & 0x3F); + } + } + out.writeBytes(buffer.byteBuffer, 0, len); + } + } + + public static String readUTF(final HornetQBuffer input) + { + StringUtilBuffer buffer = UTF8Util.getThreadLocalBuffer(); + + final int size = input.readUnsignedShort(); + + if (size > buffer.byteBuffer.length) + { + buffer.resizeByteBuffer(size); + } + + if (size > buffer.charBuffer.length) + { + buffer.resizeCharBuffer(size); + } + + if (UTF8Util.isTrace) + { + // This message is too verbose for debug, that's why we are using trace here + HornetQUtilLogger.LOGGER.trace("Reading string with utfSize=" + size); + } + + int count = 0; + int byte1, byte2, byte3; + int charCount = 0; + + input.readBytes(buffer.byteBuffer, 0, size); + + while (count < size) + { + byte1 = buffer.byteBuffer[count++]; + + if (byte1 > 0 && byte1 <= 0x7F) + { + buffer.charBuffer[charCount++] = (char)byte1; + } + else + { + int c = byte1 & 0xff; + switch (c >> 4) + { + case 0xc: + case 0xd: + byte2 = buffer.byteBuffer[count++]; + buffer.charBuffer[charCount++] = (char)((c & 0x1F) << 6 | byte2 & 0x3F); + break; + case 0xe: + byte2 = buffer.byteBuffer[count++]; + byte3 = buffer.byteBuffer[count++]; + buffer.charBuffer[charCount++] = (char)((c & 0x0F) << 12 | (byte2 & 0x3F) << 6 | (byte3 & 0x3F) << 0); + break; + default: + throw new InternalError("unhandled utf8 byte " + c); + } + } + } + + return new String(buffer.charBuffer, 0, charCount); + + } + + public static StringUtilBuffer getThreadLocalBuffer() + { + SoftReference<StringUtilBuffer> softReference = UTF8Util.currenBuffer.get(); + StringUtilBuffer value; + if (softReference == null) + { + value = new StringUtilBuffer(); + softReference = new SoftReference<StringUtilBuffer>(value); + UTF8Util.currenBuffer.set(softReference); + } + else + { + value = softReference.get(); + } + + if (value == null) + { + value = new StringUtilBuffer(); + softReference = new SoftReference<StringUtilBuffer>(value); + UTF8Util.currenBuffer.set(softReference); + } + + return value; + } + + public static void clearBuffer() + { + SoftReference<StringUtilBuffer> ref = UTF8Util.currenBuffer.get(); + if (ref.get() != null) + { + ref.clear(); + } + } + + public static int calculateUTFSize(final String str, final StringUtilBuffer stringBuffer) + { + int calculatedLen = 0; + + int stringLength = str.length(); + + if (stringLength > stringBuffer.charBuffer.length) + { + stringBuffer.resizeCharBuffer(stringLength); + } + + str.getChars(0, stringLength, stringBuffer.charBuffer, 0); + + for (int i = 0; i < stringLength; i++) + { + char c = stringBuffer.charBuffer[i]; + + if (c >= 1 && c < 0x7f) + { + calculatedLen++; + } + else if (c >= 0x800) + { + calculatedLen += 3; + } + else + { + calculatedLen += 2; + } + } + return calculatedLen; + } + + public static class StringUtilBuffer + { + + public char[] charBuffer; + + public byte[] byteBuffer; + + public void resizeCharBuffer(final int newSize) + { + if (newSize > charBuffer.length) + { + charBuffer = new char[newSize]; + } + } + + public void resizeByteBuffer(final int newSize) + { + if (newSize > byteBuffer.length) + { + byteBuffer = new byte[newSize]; + } + } + + public StringUtilBuffer() + { + this(1024, 1024); + } + + public StringUtilBuffer(final int sizeChar, final int sizeByte) + { + charBuffer = new char[sizeChar]; + byteBuffer = new byte[sizeByte]; + } + + } + +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUID.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUID.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUID.java new file mode 100644 index 0000000..35c2a0f --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUID.java @@ -0,0 +1,270 @@ +/* JUG Java Uuid Generator + * + * Copyright (c) 2002- Tatu Saloranta, [email protected] + * + * Licensed under the License specified in the file licenses/LICENSE.txt which is + * included with the source code. + * You may not use this file except in compliance with the License. + * + * 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.apache.activemq6.utils; + + +/** + * UUID represents Universally Unique Identifiers (aka Global UID in Windows + * world). UUIDs are usually generated via UUIDGenerator (or in case of 'Null + * UUID', 16 zero bytes, via static method getNullUUID()), or received from + * external systems. + * <p> + * By default class caches the string presentations of UUIDs so that description + * is only created the first time it's needed. For memory stingy applications + * this caching can be turned off (note though that if uuid.toString() is never + * called, desc is never calculated so only loss is the space allocated for the + * desc pointer... which can of course be commented out to save memory). + * <p> + * Similarly, hash code is calculated when it's needed for the first time, and + * from thereon that value is just returned. This means that using UUIDs as keys + * should be reasonably efficient. + * <p> + * UUIDs can be compared for equality, serialized, cloned and even sorted. + * Equality is a simple bit-wise comparison. Ordering (for sorting) is done by + * first ordering based on type (in the order of numeric values of types), + * secondarily by time stamp (only for time-based time stamps), and finally by + * straight numeric byte-by-byte comparison (from most to least significant + * bytes). + */ + +public final class UUID +{ + private static final String kHexChars = "0123456789abcdefABCDEF"; + + public static final byte INDEX_CLOCK_HI = 6; + + public static final byte INDEX_CLOCK_MID = 4; + + public static final byte INDEX_CLOCK_LO = 0; + + public static final byte INDEX_TYPE = 6; + + // Clock seq. & variant are multiplexed... + public static final byte INDEX_CLOCK_SEQUENCE = 8; + + public static final byte INDEX_VARIATION = 8; + + public static final byte TYPE_NULL = 0; + + public static final byte TYPE_TIME_BASED = 1; + + public static final byte TYPE_DCE = 2; // Not used + + public static final byte TYPE_NAME_BASED = 3; + + public static final byte TYPE_RANDOM_BASED = 4; + + /* + * 'Standard' namespaces defined (suggested) by UUID specs: + */ + public static final String NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; + + public static final String NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"; + + public static final String NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8"; + + public static final String NAMESPACE_X500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"; + + /* + * By default let's cache desc, can be turned off. For hash code there's no + * point in turning it off (since the int is already part of the instance + * memory allocation); if you want to save those 4 bytes (or possibly bit + * more if alignment is bad) just comment out hash caching. + */ + private static boolean sDescCaching = true; + + private final byte[] mId; + + // Both string presentation and hash value may be cached... + private transient String mDesc = null; + + private transient int mHashCode = 0; + + /** + * @param type UUID type + * @param data 16 byte UUID contents + */ + public UUID(final int type, final byte[] data) + { + mId = data; + // Type is multiplexed with time_hi: + mId[UUID.INDEX_TYPE] &= (byte) 0x0F; + mId[UUID.INDEX_TYPE] |= (byte) (type << 4); + // Variant masks first two bits of the clock_seq_hi: + mId[UUID.INDEX_VARIATION] &= (byte) 0x3F; + mId[UUID.INDEX_VARIATION] |= (byte) 0x80; + } + + public byte[] asBytes() + { + return mId; + } + + /** + * Could use just the default hash code, but we can probably create a better + * identity hash (ie. same contents generate same hash) manually, without + * sacrificing speed too much. Although multiplications with modulos would + * generate better hashing, let's use just shifts, and do 2 bytes at a time. + * <p/> + * Of course, assuming UUIDs are randomized enough, even simpler approach + * might be good enough? + * <p/> + * Is this a good hash? ... one of these days I better read more about basic + * hashing techniques I swear! + */ + private static final int[] kShifts = {3, 7, 17, 21, 29, 4, 9}; + + @Override + public int hashCode() + { + if (mHashCode == 0) + { + // Let's handle first and last byte separately: + int result = mId[0] & 0xFF; + + result |= result << 16; + result |= result << 8; + + for (int i = 1; i < 15; i += 2) + { + int curr = (mId[i] & 0xFF) << 8 | mId[i + 1] & 0xFF; + int shift = UUID.kShifts[i >> 1]; + + if (shift > 16) + { + result ^= curr << shift | curr >>> 32 - shift; + } + else + { + result ^= curr << shift; + } + } + + // and then the last byte: + int last = mId[15] & 0xFF; + result ^= last << 3; + result ^= last << 13; + + result ^= last << 27; + // Let's not accept hash 0 as it indicates 'not hashed yet': + if (result == 0) + { + mHashCode = -1; + } + else + { + mHashCode = result; + } + } + return mHashCode; + } + + @Override + public String toString() + { + /* + * Could be synchronized, but there isn't much harm in just taking our + * chances (ie. in the worst case we'll form the string more than once... + * but result is the same) + */ + + if (mDesc == null) + { + StringBuffer b = new StringBuffer(36); + + for (int i = 0; i < 16; ++i) + { + // Need to bypass hyphens: + switch (i) + { + case 4: + case 6: + case 8: + case 10: + b.append('-'); + break; + default: + // no-op + } + int hex = mId[i] & 0xFF; + b.append(UUID.kHexChars.charAt(hex >> 4)); + b.append(UUID.kHexChars.charAt(hex & 0x0f)); + } + if (!UUID.sDescCaching) + { + return b.toString(); + } + mDesc = b.toString(); + } + return mDesc; + } + + /** + * Creates a 128bit number from the String representation of {@link UUID}. + * + * @param uuid + * @return byte array that can be used to recreate a UUID instance from the given String + * representation + */ + public static byte[] stringToBytes(String uuid) + { + byte[] data = new byte[16]; + int dataIdx = 0; + try + { + for (int i = 0; i < uuid.length(); ) + { + while (uuid.charAt(i) == '-') + { + i++; + } + char c1 = uuid.charAt(i); + char c2 = uuid.charAt(i + 1); + i += 2; + int c1Bytes = Character.digit(c1, 16); + int c2Bytes = Character.digit(c2, 16); + data[dataIdx++] = (byte) ((c1Bytes << 4) + c2Bytes); + } + } + catch (RuntimeException e) + { + throw new IllegalArgumentException(e); + } + return data; + } + + /** + * Checking equality of UUIDs is easy; just compare the 128-bit number. + */ + @Override + public boolean equals(final Object o) + { + if (!(o instanceof UUID)) + { + return false; + } + byte[] otherId = ((UUID) o).mId; + byte[] thisId = mId; + for (int i = 0; i < 16; ++i) + { + if (otherId[i] != thisId[i]) + { + return false; + } + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/activemq-6/blob/23e8edd9/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUIDGenerator.java ---------------------------------------------------------------------- diff --git a/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUIDGenerator.java b/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUIDGenerator.java new file mode 100644 index 0000000..0cabf23 --- /dev/null +++ b/activemq6-commons/src/main/java/org/apache/activemq6/utils/UUIDGenerator.java @@ -0,0 +1,371 @@ +/* JUG Java Uuid Generator + * + * Copyright (c) 2002- Tatu Saloranta, [email protected] + * + * Licensed under the License specified in the file licenses/LICENSE.txt which is + * included with the source code. + * You may not use this file except in compliance with the License. + * + * 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.apache.activemq6.utils; + +import java.lang.reflect.Method; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.activemq6.api.core.SimpleString; + +public final class UUIDGenerator +{ + private static final UUIDGenerator sSingleton = new UUIDGenerator(); + + // Windows has some fake adapters that will return the same HARDWARE ADDRESS on any computer. We need to ignore those + private static final byte[][] BLACK_LIST = new byte[][]{{2, 0, 84, 85, 78, 1}}; + + /** + * Random-generator, used by various UUID-generation methods: + */ + private Random mRnd = null; + + private final Object mTimerLock = new Object(); + + private UUIDTimer mTimer = null; + + private byte[] address; + + /** + * Constructor is private to enforce singleton access. + */ + private UUIDGenerator() + { + } + + /** + * Method used for accessing the singleton generator instance. + */ + public static UUIDGenerator getInstance() + { + return UUIDGenerator.sSingleton; + } + + /* + * ///////////////////////////////////////////////////// // Configuration + * ///////////////////////////////////////////////////// + */ + + /** + * Method for getting the shared random number generator used for generating + * the UUIDs. This way the initialization cost is only taken once; access + * need not be synchronized (or in cases where it has to, SecureRandom takes + * care of it); it might even be good for getting really 'random' stuff to + * get shared access... + */ + public Random getRandomNumberGenerator() + { + /* + * Could be synchronized, but since side effects are trivial (ie. + * possibility of generating more than one SecureRandom, of which all but + * one are dumped) let's not add synchronization overhead: + */ + if (mRnd == null) + { + mRnd = new SecureRandom(); + } + return mRnd; + } + + public UUID generateTimeBasedUUID(final byte[] byteAddr) + { + byte[] contents = new byte[16]; + int pos = 10; + for (int i = 0; i < 6; ++i) + { + contents[pos + i] = byteAddr[i]; + } + + synchronized (mTimerLock) + { + if (mTimer == null) + { + mTimer = new UUIDTimer(getRandomNumberGenerator()); + } + + mTimer.getTimestamp(contents); + } + + return new UUID(UUID.TYPE_TIME_BASED, contents); + } + + public byte[] generateDummyAddress() + { + Random rnd = getRandomNumberGenerator(); + byte[] dummy = new byte[6]; + rnd.nextBytes(dummy); + /* Need to set the broadcast bit to indicate it's not a real + * address. + */ + dummy[0] |= (byte) 0x01; + + if (HornetQUtilLogger.LOGGER.isDebugEnabled()) + { + HornetQUtilLogger.LOGGER.debug("using dummy address " + UUIDGenerator.asString(dummy)); + } + return dummy; + } + + /** + * If running java 6 or above, returns {@link NetworkInterface#getHardwareAddress()}, else return {@code null}. + * The first hardware address is returned when iterating all the NetworkInterfaces + */ + public static byte[] getHardwareAddress() + { + Method getHardwareAddressMethod; + Method isUpMethod; + Method isLoopbackMethod; + Method isVirtualMethod; + try + { + getHardwareAddressMethod = NetworkInterface.class.getMethod("getHardwareAddress"); + isUpMethod = NetworkInterface.class.getMethod("isUp"); + isLoopbackMethod = NetworkInterface.class.getMethod("isLoopback"); + isVirtualMethod = NetworkInterface.class.getMethod("isVirtual"); + // check if we have enough security permissions to create and shutdown an executor + ExecutorService executor = Executors.newFixedThreadPool(0); + executor.shutdownNow(); + } + catch (Throwable t) + { + // not on Java 6 or not enough security permission + return null; + } + + try + { + List<NetworkInterface> ifaces = getAllNetworkInterfaces(); + + if (ifaces.size() == 0) + { + return null; + } + + byte[] address = findFirstMatchingHardwareAddress(ifaces, + getHardwareAddressMethod, + isUpMethod, + isLoopbackMethod, + isVirtualMethod); + if (address != null) + { + if (HornetQUtilLogger.LOGGER.isDebugEnabled()) + { + HornetQUtilLogger.LOGGER.debug("using hardware address " + UUIDGenerator.asString(address)); + } + return address; + } + return null; + } + catch (Exception e) + { + return null; + } + } + + public SimpleString generateSimpleStringUUID() + { + return new SimpleString(generateStringUUID()); + } + + public UUID generateUUID() + { + byte[] address = getAddressBytes(); + + UUID uid = generateTimeBasedUUID(address); + + return uid; + } + + public String generateStringUUID() + { + byte[] address = getAddressBytes(); + + if (address == null) + { + return java.util.UUID.randomUUID().toString(); + } + else + { + return generateTimeBasedUUID(address).toString(); + } + } + + public static byte[] getZeroPaddedSixBytes(final byte[] bytes) + { + if (bytes == null) + { + return null; + } + if (bytes.length > 0 && bytes.length <= 6) + { + if (bytes.length == 6) + { + return bytes; + } + else + { + // pad with zeroes to have a 6-byte array + byte[] paddedAddress = new byte[6]; + System.arraycopy(bytes, 0, paddedAddress, 0, bytes.length); + for (int i = bytes.length; i < 6; i++) + { + paddedAddress[i] = 0; + } + return paddedAddress; + } + } + return null; + } + + // Private ------------------------------------------------------- + + private static boolean isBlackList(final byte[] address) + { + for (byte[] blackList : UUIDGenerator.BLACK_LIST) + { + if (Arrays.equals(address, blackList)) + { + return true; + } + } + return false; + } + + private byte[] getAddressBytes() + { + if (address == null) + { + address = UUIDGenerator.getHardwareAddress(); + if (address == null) + { + address = generateDummyAddress(); + } + } + + return address; + } + + private static String asString(final byte[] bytes) + { + if (bytes == null) + { + return null; + } + + StringBuilder s = new StringBuilder(); + for (int i = 0; i < bytes.length - 1; i++) + { + s.append(Integer.toHexString(bytes[i])); + s.append(":"); + } + s.append(bytes[bytes.length - 1]); + return s.toString(); + } + + private static List<NetworkInterface> getAllNetworkInterfaces() + { + Enumeration<NetworkInterface> networkInterfaces; + try + { + networkInterfaces = NetworkInterface.getNetworkInterfaces(); + + List<NetworkInterface> ifaces = new ArrayList<NetworkInterface>(); + while (networkInterfaces.hasMoreElements()) + { + ifaces.add(networkInterfaces.nextElement()); + } + return ifaces; + } + catch (SocketException e) + { + return Collections.emptyList(); + } + } + + private static byte[] findFirstMatchingHardwareAddress(List<NetworkInterface> ifaces, + final Method getHardwareAddressMethod, + final Method isUpMethod, + final Method isLoopbackMethod, + final Method isVirtualMethod) + { + ExecutorService executor = Executors.newFixedThreadPool(ifaces.size()); + Collection<Callable<byte[]>> tasks = new ArrayList<Callable<byte[]>>(ifaces.size()); + + for (final NetworkInterface networkInterface : ifaces) + { + tasks.add(new Callable<byte[]>() + { + public byte[] call() throws Exception + { + boolean up = (Boolean) isUpMethod.invoke(networkInterface); + boolean loopback = (Boolean) isLoopbackMethod.invoke(networkInterface); + boolean virtual = (Boolean) isVirtualMethod.invoke(networkInterface); + + if (loopback || virtual || !up) + { + throw new Exception("not suitable interface"); + } + + Object res = getHardwareAddressMethod.invoke(networkInterface); + if (res != null && res instanceof byte[]) + { + + byte[] address = (byte[]) res; + byte[] paddedAddress = UUIDGenerator.getZeroPaddedSixBytes(address); + + if (UUIDGenerator.isBlackList(address)) + { + throw new Exception("black listed address"); + } + + if (paddedAddress != null) + { + return paddedAddress; + } + } + + throw new Exception("invalid network interface"); + } + }); + } + try + { + // we wait 5 seconds to get the first matching hardware address. After that, we give up and return null + byte[] address = executor.invokeAny(tasks, 5, TimeUnit.SECONDS); + return address; + } + catch (Exception e) + { + return null; + } + finally + { + executor.shutdownNow(); + } + } +}
