package org.infinispan.hibernate.search; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.hibernate.cfg.Environment; import org.hibernate.search.engine.spi.EntityIndexBinding; import org.hibernate.search.indexes.spi.DirectoryBasedIndexManager; import org.hibernate.search.spi.SearchIntegrator; import org.hibernate.search.test.util.FullTextSessionBuilder; import org.infinispan.hibernate.search.impl.DefaultCacheManagerService; import org.infinispan.hibernate.search.spi.InfinispanDirectoryProvider; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.remoting.transport.Address; /** * Helpers to setup several instances of Hibernate Search using clustering to connect the index, and sharing the same H2 * database instance. * * @author Sanne Grinovero <sanne@hibernate.org> (C) 2011 Red Hat Inc. */ public final class ClusterTestHelper { private ClusterTestHelper() { //not allowed } /** * Create a clustered Hibernate Search instance. Note the configuration used is not optimal for performance, we do * this on purpose to make sure we test with an highly fragmented index. The backing CacheManager will be started, * but didn't necessarily join the existing nodes. * * @param entityTypes the set of indexed classes * @param exclusiveIndexUse set to true to enable the EXCLUSIVE_INDEX_USE configuration option * @return a started FullTextSessionBuilder */ public static FullTextSessionBuilder createClusterNode(Set<Class<?>> entityTypes, boolean exclusiveIndexUse) { return createClusterNode(entityTypes, exclusiveIndexUse, true, false); } /** * As {@link #createClusterNode(Set, boolean)} but allows more options * * @param entityTypes the set of indexed classes * @param exclusiveIndexUse set to true to enable the EXCLUSIVE_INDEX_USE configuration option * @param setInfinispanDirectory set to true to enable the directory_provider setting to 'infinispan' * @param setInfinispanIndexManager set to true to enable the indexmanager setting to 'infinispan' * @return */ public static FullTextSessionBuilder createClusterNode(Set<Class<?>> entityTypes, boolean exclusiveIndexUse, boolean setInfinispanDirectory, boolean setInfinispanIndexManager) { FullTextSessionBuilder node = new FullTextSessionBuilder(); if (setInfinispanDirectory) { node.setProperty("hibernate.search.default.directory_provider", "infinispan"); } if (setInfinispanIndexManager) { node.setProperty("hibernate.search.default.indexmanager", "infinispan"); } // fragment on every 13 bytes: don't use this on a real case! // only done to make sure we generate lots of small fragments. node.setProperty("hibernate.search.default.indexwriter.chunk_size", "13"); //Override the JGroups configuration to use the testing loopback stack node.setProperty(DefaultCacheManagerService.INFINISPAN_TRANSPORT_OVERRIDE_RESOURCENAME, "testing-flush-loopback.xml"); // this schema is shared across nodes, so don't drop it on shutdown: node.setProperty(Environment.HBM2DDL_AUTO, "create"); // if we should allow aggressive index locking: node.setProperty("hibernate.search.default." + org.hibernate.search.cfg.Environment.EXCLUSIVE_INDEX_USE, String.valueOf(exclusiveIndexUse)); // share the same in-memory database connection pool node.setProperty( Environment.CONNECTION_PROVIDER, org.infinispan.hibernate.search.ClusterSharedConnectionProvider.class.getName() ); for (Class<?> entityType : entityTypes) { node.addAnnotatedClass(entityType); } return node.build(); } /** * delegates {@link #waitMembersCount(FullTextSessionBuilder, Class, int, long, TimeUnit)} with 10s. */ public static void waitMembersCount(FullTextSessionBuilder node, Class<?> entityType, int expectedSize) { waitMembersCount(node, entityType, expectedSize, 10, TimeUnit.SECONDS); } /** * Wait some time for the cluster to form * * @param node Node to be checked * @param entityType An entity for check * @param expectedSize Expected size of the cluster * @param timeout Desired timeout * @param timeoutUnit Timeout units */ public static void waitMembersCount(FullTextSessionBuilder node, Class<?> entityType, int expectedSize, long timeout, TimeUnit timeoutUnit) { long endTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeout, timeoutUnit); int currentSize = 0; do { if (System.currentTimeMillis() > endTime) { throw new AssertionError("Timeout when waiting for desired number of nodes. Expected: " + expectedSize + ", got: " + currentSize); } Thread.yield(); currentSize = clusterSize(node, entityType); } while(currentSize != expectedSize); } /** * Counts the number of nodes in the cluster on this node * * @param node the FullTextSessionBuilder representing the current node * @return the number of nodes as seen by the current node */ public static int clusterSize(FullTextSessionBuilder node, Class<?> entityType) { SearchIntegrator integrator = node.getSearchFactory().unwrap(SearchIntegrator.class); EntityIndexBinding indexBinding = integrator.getIndexBinding(entityType); DirectoryBasedIndexManager indexManager = (DirectoryBasedIndexManager) indexBinding.getIndexManagers()[0]; InfinispanDirectoryProvider directoryProvider = (InfinispanDirectoryProvider) indexManager.getDirectoryProvider(); EmbeddedCacheManager cacheManager = directoryProvider.getCacheManager(); List<Address> members = cacheManager.getMembers(); return members.size(); } }