[hibernate-commits] Hibernate SVN: r19897 - in search/trunk: hibernate-search/src/main/java/org/hibernate/search and 11 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Jul 5 10:46:02 EDT 2010


Author: hardy.ferentschik
Date: 2010-07-05 10:46:01 -0400 (Mon, 05 Jul 2010)
New Revision: 19897

Added:
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfo.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfoMBean.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrl.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrlMBean.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/JNDIHelper.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/ConfigInfoMBeanTest.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/Counter.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/IndexCtrlMBeanTest.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/NoMBeansEnabledTest.java
Removed:
   search/trunk/hibernate-search/src/test/resources/jndi.properties
Modified:
   search/trunk/hibernate-search-testing/src/main/java/org/hibernate/search/test/SearchTestCase.java
   search/trunk/hibernate-search/pom.xml
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/Environment.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/Search.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/LuceneIndexingParameters.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/ImmutableSearchFactory.java
   search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/SearchTestCase.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/master/JMSMasterTest.java
   search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/slave/JMSSlaveTest.java
Log:
HSEARCH-278 (Create a Search Statistic JMX Bean)
* Added two MBeans - HibernateSearchConfigInfoMBean and HibernateSearchIndexCtrlMBean - with their implementing classes and tests
* Introduced simple-jndi as new dependecy for testing of MBean functionaly
* Refactored all JNDI based tests to not rely on jndi.properties, but rather pass jndi related setting as config parameters
* Some minor formatting

Modified: search/trunk/hibernate-search/pom.xml
===================================================================
--- search/trunk/hibernate-search/pom.xml	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/pom.xml	2010-07-05 14:46:01 UTC (rev 19897)
@@ -22,7 +22,9 @@
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
@@ -76,11 +78,11 @@
             <artifactId>jms</artifactId>
             <scope>provided</scope>
         </dependency>
-		<dependency>
-			<groupId>javax.annotation</groupId>
-			<artifactId>jsr250-api</artifactId>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>jsr250-api</artifactId>
             <scope>provided</scope>
-		</dependency>
+        </dependency>
         <dependency>
             <groupId>org.hibernate</groupId>
             <artifactId>hibernate-entitymanager</artifactId>
@@ -96,6 +98,12 @@
             <artifactId>activemq-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>simple-jndi</groupId>
+            <artifactId>simple-jndi</artifactId>
+            <version>0.11.4</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -139,6 +147,10 @@
                             <name>java.net.preferIPv4Stack</name>
                             <value>true</value>
                         </property>
+                        <property>
+                            <name>com.sun.management.jmxremote</name>
+                            <value>true</value>
+                        </property>
                     </systemProperties>
                     <excludes>
                         <exclude>**/classloading/*.java</exclude>
@@ -187,7 +199,10 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-release-plugin</artifactId>
                 <configuration>
-                    <goals>deploy javadoc:javadoc org.jboss.maven.plugins:maven-jdocbook-plugin:2.2.3:translate org.jboss.maven.plugins:maven-jdocbook-plugin:2.2.3:resources org.jboss.maven.plugins:maven-jdocbook-plugin:2.2.3:generate assembly:assembly</goals>
+                    <goals>deploy javadoc:javadoc org.jboss.maven.plugins:maven-jdocbook-plugin:2.2.3:translate
+                        org.jboss.maven.plugins:maven-jdocbook-plugin:2.2.3:resources
+                        org.jboss.maven.plugins:maven-jdocbook-plugin:2.2.3:generate assembly:assembly
+                    </goals>
                 </configuration>
             </plugin>
             <plugin>

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/Environment.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/Environment.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/Environment.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -66,12 +66,12 @@
 	 * default 1
 	 */
 	public static final String WORKER_THREADPOOL_SIZE = Environment.WORKER_PREFIX + "thread_pool.size";
-	
+
 	/**
 	 * Size of the buffer queue (besides the thread pool size)
 	 * <ul>
-	 * 	<li>only used then execution is async</li>
-	 * 	<li>default infinite</li>
+	 * <li>only used then execution is async</li>
+	 * <li>default infinite</li>
 	 * </ul>
 	 */
 	public static final String WORKER_WORKQUEUE_SIZE = Environment.WORKER_PREFIX + "buffer_queue.max";
@@ -80,28 +80,28 @@
 	 * define the reader prefix
 	 */
 	public static final String READER_PREFIX = "hibernate.search.reader.";
-	
+
 	/**
 	 * define the reader strategy used
 	 */
 	public static final String READER_STRATEGY = READER_PREFIX + "strategy";
-	
+
 	/**
 	 * filter caching strategy class (must have a no-arg constructor and implement FilterCachingStrategy)
 	 */
 	public static final String FILTER_CACHING_STRATEGY = "hibernate.search.filter.cache_strategy";
-	
+
 	/**
 	 * number of docidresults cached in hard reference.
 	 */
 	public static final String CACHE_DOCIDRESULTS_SIZE = "hibernate.search.filter.cache_docidresults.size";
-	
+
 	/**
 	 * batch backend implementation class (must have a no-arg constructor and implement BatchBackend)
 	 * also prefix for configuration settings of the batch backend
 	 */
 	public static final String BATCH_BACKEND = "hibernate.search.batchbackend";
-	
+
 	/**
 	 * When set to true a lock on the index will not be released until the
 	 * SearchFactory (or SessionFactory) is closed.
@@ -111,17 +111,19 @@
 	 */
 	public static final String EXCLUSIVE_INDEX_USE = "exclusive_index_use";
 
-	/**
-	 *
-	 */
 	public static final String MODEL_MAPPING = "hibernate.search.model_mapping";
 
-	
 	/**
 	 * Set to a fully qualified classname of a type implementing org.hibernate.search.exception.ErrorHandler
 	 * to override the error strategy used during processing of the Lucene updates.
 	 * Default is to log errors.
 	 */
 	public static final String ERROR_HANDLER = "hibernate.search.error_handler";
-	
+
+	/**
+	 * If set to {@code true} the JMX Statistics bean gets enabled. For all other values the bean does not
+	 * get enabled.
+	 */
+	public static final String JMX_ENABLED = "hibernate.search.jmx_enabled";
+
 }

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/Search.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/Search.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/Search.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -29,7 +29,7 @@
 
 /**
  * Helper class to get a FullTextSession out of a regular session.
- * 
+ *
  * @author Emmanuel Bernard
  * @author Hardy Ferentschik
  */
@@ -39,19 +39,19 @@
 	}
 
 	public static FullTextSession getFullTextSession(Session session) {
-		if (session instanceof FullTextSessionImpl) {
-			return (FullTextSession) session;
+		if ( session instanceof FullTextSessionImpl ) {
+			return ( FullTextSession ) session;
 		}
 		else {
-			return new FullTextSessionImpl(session);
+			return new FullTextSessionImpl( session );
 		}
 	}
-	
+
 	/**
 	 * @deprecated As of release 3.1.0, replaced by {@link #getFullTextSession(Session)}
 	 */
-	@Deprecated 
+	@Deprecated
 	public static FullTextSession createFullTextSession(Session session) {
-		return getFullTextSession(session);
+		return getFullTextSession( session );
 	}
 }

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/LuceneIndexingParameters.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/LuceneIndexingParameters.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/LuceneIndexingParameters.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -107,6 +107,16 @@
 		return batchIndexParameters;
 	}
 
+	@Override
+	public String toString() {
+		final StringBuilder sb = new StringBuilder();
+		sb.append( "LuceneIndexingParameters" );
+		sb.append( "{batchIndexParameters=" ).append( batchIndexParameters );
+		sb.append( ", transactionIndexParameters=" ).append( transactionIndexParameters );
+		sb.append( '}' );
+		return sb.toString();
+	}
+
 	public static class ParameterSet implements Serializable {
 		
 		private static final long serialVersionUID = -6121723702279869524L;
@@ -182,9 +192,17 @@
 				return false;
 			return true;
 		}
-		
- 	}
 
+		@Override
+		public String toString() {
+			final StringBuilder sb = new StringBuilder();
+			sb.append( "ParameterSet" );
+			sb.append( "{parameters=" ).append( parameters );
+			sb.append( '}' );
+			return sb.toString();
+		}
+	}
+
 	public void applyToWriter(IndexWriter writer, boolean batch) {
 		if ( batch ) {
 			getBatchIndexParameters().applyToWriter( writer );

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -1,8 +1,7 @@
-/* $Id$
- * 
+/*
  * Hibernate, Relational Persistence for Idiomatic Java
  * 
- * Copyright (c) 2009, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
  * indicated by the @author tags or express copyright attribution
  * statements applied by the authors.  All third-party contributions are
  * distributed under license by Red Hat, Inc.
@@ -24,26 +23,25 @@
  */
 package org.hibernate.search.backend.impl.jms;
 
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 import javax.jms.Queue;
 import javax.jms.QueueConnectionFactory;
-import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
 
 import org.hibernate.search.Environment;
-import org.hibernate.search.backend.UpdatableBackendQueueProcessorFactory;
-import org.hibernate.search.spi.WorkerBuildContext;
 import org.hibernate.search.SearchException;
 import org.hibernate.search.backend.LuceneWork;
+import org.hibernate.search.backend.UpdatableBackendQueueProcessorFactory;
+import org.hibernate.search.spi.WorkerBuildContext;
 import org.hibernate.search.store.DirectoryProvider;
+import org.hibernate.search.util.JNDIHelper;
 
 /**
  * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
  */
 public class JMSBackendQueueProcessorFactory implements UpdatableBackendQueueProcessorFactory {
 	private String jmsQueueName;
@@ -56,7 +54,7 @@
 	public static final String JMS_QUEUE = Environment.WORKER_PREFIX + "jms.queue";
 
 	public void initialize(Properties props, WorkerBuildContext context) {
-		//TODO proper exception if jms queues and connecitons are not there
+		//TODO proper exception if jms queues and connections are not there
 		this.properties = props;
 		this.jmsConnectionFactoryName = props.getProperty( JMS_CONNECTION_FACTORY );
 		this.jmsQueueName = props.getProperty( JMS_QUEUE );
@@ -86,67 +84,30 @@
 	}
 
 	public void prepareJMSTools() {
-		if ( jmsQueue != null && factory != null ) return;
+		if ( jmsQueue != null && factory != null ) {
+			return;
+		}
 		try {
-			InitialContext initialContext = getInitialContext( properties );
-			factory = (QueueConnectionFactory) initialContext.lookup( jmsConnectionFactoryName );
-			jmsQueue = (Queue) initialContext.lookup( jmsQueueName );
+			InitialContext initialContext = JNDIHelper.getInitialContext( properties, JNDI_PREFIX );
+			factory = ( QueueConnectionFactory ) initialContext.lookup( jmsConnectionFactoryName );
+			jmsQueue = ( Queue ) initialContext.lookup( jmsQueueName );
 
 		}
-		catch (NamingException e) {
-			throw new SearchException( "Unable to lookup Search queue ("
-					+ ( jmsQueueName != null ?
-					jmsQueueName :
-					"null" ) + ") and connection factory ("
-					+ ( jmsConnectionFactoryName != null ?
-					jmsConnectionFactoryName :
-					"null" ) + ")",
+		catch ( NamingException e ) {
+			throw new SearchException(
+					"Unable to lookup Search queue ("
+							+ ( jmsQueueName != null ?
+							jmsQueueName :
+							"null" ) + ") and connection factory ("
+							+ ( jmsConnectionFactoryName != null ?
+							jmsConnectionFactoryName :
+							"null" ) + ")",
 					e
 			);
 		}
 	}
 
-	private InitialContext getInitialContext(Properties properties) throws NamingException {
-		Properties jndiProps = getJndiProperties( properties );
-		if ( jndiProps.size() == 0 ) {
-			return new InitialContext();
-		}
-		else {
-			return new InitialContext( jndiProps );
-		}
-	}
-
-	private static Properties getJndiProperties(Properties properties) {
-
-		HashSet specialProps = new HashSet();
-		specialProps.add( JNDI_PREFIX + "class" );
-		specialProps.add( JNDI_PREFIX + "url" );
-
-		Iterator iter = properties.keySet().iterator();
-		Properties result = new Properties();
-		while ( iter.hasNext() ) {
-			String prop = (String) iter.next();
-			if ( prop.indexOf( JNDI_PREFIX ) > -1 && !specialProps.contains( prop ) ) {
-				result.setProperty(
-						prop.substring( JNDI_PREFIX.length() ),
-						properties.getProperty( prop )
-				);
-			}
-		}
-
-		String jndiClass = properties.getProperty( JNDI_PREFIX + "class" );
-		String jndiURL = properties.getProperty( JNDI_PREFIX + "url" );
-		// we want to be able to just use the defaults,
-		// if JNDI environment properties are not supplied
-		// so don't put null in anywhere
-		if ( jndiClass != null ) result.put( Context.INITIAL_CONTEXT_FACTORY, jndiClass );
-		if ( jndiURL != null ) result.put( Context.PROVIDER_URL, jndiURL );
-
-		return result;
-	}
-
 	public void close() {
 		// no need to release anything
 	}
-
 }

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/ImmutableSearchFactory.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/ImmutableSearchFactory.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/ImmutableSearchFactory.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -35,9 +35,6 @@
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.search.Similarity;
-import org.hibernate.search.spi.internals.DirectoryProviderData;
-import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
-import org.hibernate.search.spi.internals.StateSearchFactoryImplementor;
 import org.slf4j.Logger;
 
 import org.hibernate.annotations.common.AssertionFailure;
@@ -63,6 +60,9 @@
 import org.hibernate.search.query.dsl.v2.impl.ConnectedQueryContextBuilder;
 import org.hibernate.search.reader.ReaderProvider;
 import org.hibernate.search.spi.WorkerBuildContext;
+import org.hibernate.search.spi.internals.DirectoryProviderData;
+import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
+import org.hibernate.search.spi.internals.StateSearchFactoryImplementor;
 import org.hibernate.search.store.DirectoryProvider;
 import org.hibernate.search.store.optimization.OptimizerStrategy;
 import org.hibernate.search.util.LoggerFactory;
@@ -71,7 +71,7 @@
 
 /**
  * This implementation is never directly exposed to the user, it is always wrapped into a {@link org.hibernate.search.impl.MutableSearchFactory}
- * 
+ *
  * @author Emmanuel Bernard
  */
 public class ImmutableSearchFactory implements StateSearchFactoryImplementor, WorkerBuildContext {
@@ -122,7 +122,6 @@
 		this.worker = cfg.worker;
 	}
 
-
 	public BackendQueueProcessorFactory getBackendQueueProcessorFactory() {
 		return backendQueueProcessorFactory;
 	}
@@ -202,7 +201,7 @@
 	}
 
 	public void setBackendQueueProcessorFactory(BackendQueueProcessorFactory backendQueueProcessorFactory) {
-		throw new AssertionFailure( "ImmutableSearchFactory is immutable: should never be called");
+		throw new AssertionFailure( "ImmutableSearchFactory is immutable: should never be called" );
 	}
 
 	public OptimizerStrategy getOptimizerStrategy(DirectoryProvider<?> provider) {
@@ -296,7 +295,7 @@
 	public Set<Class<?>> getIndexedTypesPolymorphic(Class<?>[] classes) {
 		return indexHierarchy.getIndexedClasses( classes );
 	}
-	
+
 	public BatchBackend makeBatchBackend(MassIndexerProgressMonitor progressMonitor) {
 		BatchBackend batchBackend;
 		String impl = configurationProperties.getProperty( Environment.BATCH_BACKEND );
@@ -304,18 +303,23 @@
 			batchBackend = new LuceneBatchBackend();
 		}
 		else {
-			batchBackend = PluginLoader.instanceFromName( BatchBackend.class, impl, ImmutableSearchFactory.class,
-					"batchbackend" );
+			batchBackend = PluginLoader.instanceFromName(
+					BatchBackend.class, impl, ImmutableSearchFactory.class,
+					"batchbackend"
+			);
 		}
 		Properties batchBackendConfiguration = new MaskedProperty(
-				this.configurationProperties, Environment.BATCH_BACKEND );
+				this.configurationProperties, Environment.BATCH_BACKEND
+		);
 		batchBackend.initialize( batchBackendConfiguration, progressMonitor, this );
 		return batchBackend;
 	}
 
 	public Similarity getSimilarity(DirectoryProvider<?> provider) {
 		Similarity similarity = dirProviderData.get( provider ).getSimilarity();
-		if ( similarity == null ) throw new SearchException( "Assertion error: a similarity should be defined for each provider" );
+		if ( similarity == null ) {
+			throw new SearchException( "Assertion error: a similarity should be defined for each provider" );
+		}
 		return similarity;
 	}
 

Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/impl/SearchFactoryBuilder.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -1,6 +1,30 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
 package org.hibernate.search.impl;
 
 import java.beans.Introspector;
+import java.lang.management.ManagementFactory;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -12,16 +36,12 @@
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.search.Similarity;
-
-import org.hibernate.search.backend.UpdatableBackendQueueProcessorFactory;
-import org.hibernate.search.spi.WorkerBuildContext;
-import org.hibernate.search.spi.WritableBuildContext;
-import org.hibernate.search.spi.internals.DirectoryProviderData;
-import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
-import org.hibernate.search.spi.internals.StateSearchFactoryImplementor;
 import org.slf4j.Logger;
 
 import org.hibernate.annotations.common.reflection.MetadataProvider;
@@ -40,6 +60,7 @@
 import org.hibernate.search.annotations.Key;
 import org.hibernate.search.backend.BackendQueueProcessorFactory;
 import org.hibernate.search.backend.LuceneIndexingParameters;
+import org.hibernate.search.backend.UpdatableBackendQueueProcessorFactory;
 import org.hibernate.search.backend.Worker;
 import org.hibernate.search.backend.WorkerFactory;
 import org.hibernate.search.backend.configuration.ConfigurationParseHelper;
@@ -56,8 +77,16 @@
 import org.hibernate.search.filter.FilterCachingStrategy;
 import org.hibernate.search.filter.MRUFilterCachingStrategy;
 import org.hibernate.search.filter.ShardSensitiveOnlyFilter;
+import org.hibernate.search.jmx.HibernateSearchConfigInfo;
+import org.hibernate.search.jmx.HibernateSearchConfigInfoMBean;
+import org.hibernate.search.jmx.HibernateSearchIndexCtrl;
 import org.hibernate.search.reader.ReaderProvider;
 import org.hibernate.search.reader.ReaderProviderFactory;
+import org.hibernate.search.spi.WorkerBuildContext;
+import org.hibernate.search.spi.WritableBuildContext;
+import org.hibernate.search.spi.internals.DirectoryProviderData;
+import org.hibernate.search.spi.internals.PolymorphicIndexHierarchy;
+import org.hibernate.search.spi.internals.StateSearchFactoryImplementor;
 import org.hibernate.search.store.DirectoryProvider;
 import org.hibernate.search.store.DirectoryProviderFactory;
 import org.hibernate.search.store.optimization.OptimizerStrategy;
@@ -67,8 +96,10 @@
 import org.hibernate.util.StringHelper;
 
 /**
- * Build a search factory
+ * Build a search factory.
+ *
  * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
  */
 public class SearchFactoryBuilder {
 	private static final Logger log = LoggerFactory.make();
@@ -111,25 +142,75 @@
 	Map<DirectoryProvider, LuceneIndexingParameters> dirProviderIndexingParams;
 
 	public SearchFactoryImplementor buildSearchFactory() {
-		if (rootFactory == null) {
-			if (classes.size() > 0) {
-				throw new SearchException( "Cannot add a class if the original SearchFactory is not passed");
+		SearchFactoryImplementor searchFactoryImplementor;
+		if ( rootFactory == null ) {
+			if ( classes.size() > 0 ) {
+				throw new SearchException( "Cannot add a class if the original SearchFactory is not passed" );
 			}
-			return buildNewSearchFactory();
+			searchFactoryImplementor = buildNewSearchFactory();
 		}
 		else {
-			return buildIncrementalSearchFactory();
+			searchFactoryImplementor = buildIncrementalSearchFactory();
 		}
+
+		String enableJMX = configurationProperties.getProperty( Environment.JMX_ENABLED );
+		if ( "true".equalsIgnoreCase( enableJMX ) ) {
+			enableJMXStatistics( searchFactoryImplementor );
+		}
+		return searchFactoryImplementor;
 	}
 
+	private void enableJMXStatistics(SearchFactoryImplementor searchFactoryImplementor) {
+		HibernateSearchConfigInfo statsBean = new HibernateSearchConfigInfo( searchFactoryImplementor );
+		ObjectName name = createObjectName( HibernateSearchConfigInfoMBean.CONFIG_MBEAN_OBJECT_NAME );
+		registerMBean( statsBean, name );
+
+		// if we have a JNDI bound SessionFactory we can also enable the index control bean
+		if ( StringHelper.isNotEmpty( configurationProperties.getProperty( "hibernate.session_factory_name" ) ) ) {
+			HibernateSearchIndexCtrl indexCtrlBean = new HibernateSearchIndexCtrl( configurationProperties );
+			name = createObjectName( HibernateSearchIndexCtrl.INDEX_CTRL_MBEAN_OBJECT_NAME );
+			registerMBean( indexCtrlBean, name );
+		}
+	}
+
+	private ObjectName createObjectName(String name) {
+		ObjectName objectName;
+		try {
+			objectName = new ObjectName( name );
+		}
+		catch ( MalformedObjectNameException e ) {
+			throw new SearchException( "Invalid JMX Bean name: " + name, e );
+		}
+		return objectName;
+	}
+
+	private void registerMBean(Object stats, ObjectName name) {
+		MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+		if ( mbs.isRegistered( name ) ) {
+			try {
+				mbs.unregisterMBean( name );
+			}
+			catch ( Exception e ) {
+				log.warn( "Unable to un-register existing MBean: " + name, e );
+			}
+		}
+
+		try {
+			mbs.registerMBean( stats, name );
+		}
+		catch ( Exception e ) {
+			throw new SearchException( "Unable to enable MBean for Hibernate Search", e );
+		}
+	}
+
 	private SearchFactoryImplementor buildIncrementalSearchFactory() {
 		removeClassesAlreadyManaged();
-		if (classes.size() == 0) {
+		if ( classes.size() == 0 ) {
 			return rootFactory;
 		}
 
 		BuildContext buildContext = new BuildContext();
-		copyStateFromOldFactory(rootFactory);
+		copyStateFromOldFactory( rootFactory );
 		//TODO we don't keep the reflectionManager. Is that an issue?
 		IncrementalSearchConfiguration cfg = new IncrementalSearchConfiguration( classes, configurationProperties );
 		reflectionManager = getReflectionManager( cfg );
@@ -152,7 +233,7 @@
 
 		//update backend
 		final BackendQueueProcessorFactory backend = this.backendQueueProcessorFactory;
-		if ( backend instanceof UpdatableBackendQueueProcessorFactory) {
+		if ( backend instanceof UpdatableBackendQueueProcessorFactory ) {
 			final UpdatableBackendQueueProcessorFactory updatableBackend = ( UpdatableBackendQueueProcessorFactory ) backend;
 			updatableBackend.updateDirectoryProviders( this.dirProviderData.keySet(), buildContext );
 		}
@@ -168,13 +249,13 @@
 	private void removeClassesAlreadyManaged() {
 		Set<Class<?>> remove = new HashSet<Class<?>>();
 		final Map<Class<?>, DocumentBuilderContainedEntity<?>> containedEntities = rootFactory.getDocumentBuildersContainedEntities();
-		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> indexedEntities = rootFactory.getDocumentBuildersIndexedEntities();	
-		for (Class<?> entity : classes) {
-			if ( indexedEntities.containsKey( entity ) || containedEntities.containsKey(entity) ) {
+		final Map<Class<?>, DocumentBuilderIndexedEntity<?>> indexedEntities = rootFactory.getDocumentBuildersIndexedEntities();
+		for ( Class<?> entity : classes ) {
+			if ( indexedEntities.containsKey( entity ) || containedEntities.containsKey( entity ) ) {
 				remove.add( entity );
 			}
 		}
-		for(Class<?> entity : remove) {
+		for ( Class<?> entity : remove ) {
 			classes.remove( entity );
 		}
 	}
@@ -202,16 +283,18 @@
 
 		configurationProperties = cfg.getProperties();
 		errorHandler = createErrorHandler( configurationProperties );
-		reflectionManager = getReflectionManager(cfg);
+		reflectionManager = getReflectionManager( cfg );
 		BuildContext buildContext = new BuildContext();
 
-		final SearchMapping mapping = SearchMappingBuilder.getSearchMapping(cfg);
-		if ( mapping != null) {
-			if ( ! ( reflectionManager instanceof MetadataProviderInjector )) {
-				throw new SearchException("Programmatic mapping model used but ReflectionManager does not implement "
-						+ MetadataProviderInjector.class.getName() );
+		final SearchMapping mapping = SearchMappingBuilder.getSearchMapping( cfg );
+		if ( mapping != null ) {
+			if ( !( reflectionManager instanceof MetadataProviderInjector ) ) {
+				throw new SearchException(
+						"Programmatic mapping model used but ReflectionManager does not implement "
+								+ MetadataProviderInjector.class.getName()
+				);
 			}
-			MetadataProviderInjector injector = (MetadataProviderInjector) reflectionManager;
+			MetadataProviderInjector injector = ( MetadataProviderInjector ) reflectionManager;
 			MetadataProvider original = injector.getMetadataProvider();
 			injector.setMetadataProvider( new MappingModelMetadataProvider( original, mapping ) );
 		}
@@ -248,10 +331,12 @@
 				DocumentBuilderIndexedEntity<?> documentBuilder = documentBuildersIndexedEntities.get( indexedType );
 				Similarity similarity = documentBuilder.getSimilarity();
 				Similarity prevSimilarity = directoryConfiguration.getSimilarity();
-				if ( prevSimilarity != null && ! prevSimilarity.getClass().equals( similarity.getClass() ) ) {
-					throw new SearchException( "Multiple entities are sharing the same index but are declaring an " +
-							"inconsistent Similarity. When overrriding default Similarity make sure that all types sharing a same index " +
-							"declare the same Similarity implementation." );
+				if ( prevSimilarity != null && !prevSimilarity.getClass().equals( similarity.getClass() ) ) {
+					throw new SearchException(
+							"Multiple entities are sharing the same index but are declaring an " +
+									"inconsistent Similarity. When overrriding default Similarity make sure that all types sharing a same index " +
+									"declare the same Similarity implementation."
+					);
 				}
 				else {
 					directoryConfiguration.setSimilarity( similarity );
@@ -267,8 +352,10 @@
 			filterCachingStrategy = new MRUFilterCachingStrategy();
 		}
 		else {
-			filterCachingStrategy = PluginLoader.instanceFromName( FilterCachingStrategy.class,
-					impl, ImmutableSearchFactory.class, "filterCachingStrategy" );
+			filterCachingStrategy = PluginLoader.instanceFromName(
+					FilterCachingStrategy.class,
+					impl, ImmutableSearchFactory.class, "filterCachingStrategy"
+			);
 		}
 		filterCachingStrategy.initialize( properties );
 		return filterCachingStrategy;
@@ -289,20 +376,21 @@
 	 * Initialize the document builder
 	 * This algorithm seems to be safe for incremental search factories.
 	 */
+
 	private void initDocumentBuilders(SearchConfiguration cfg, ReflectionManager reflectionManager, BuildContext buildContext) {
 		ConfigContext context = new ConfigContext( cfg );
 		Iterator<Class<?>> iter = cfg.getClassMappings();
 		DirectoryProviderFactory factory = new DirectoryProviderFactory();
 
-		initProgrammaticAnalyzers(context, reflectionManager);
-		initProgrammaticallyDefinedFilterDef(reflectionManager);
+		initProgrammaticAnalyzers( context, reflectionManager );
+		initProgrammaticallyDefinedFilterDef( reflectionManager );
 
 		while ( iter.hasNext() ) {
 			Class<?> mappedClass = iter.next();
 			if ( mappedClass == null ) {
 				continue;
 			}
-			@SuppressWarnings( "unchecked" )
+			@SuppressWarnings("unchecked")
 			XClass mappedXClass = reflectionManager.toXClass( mappedClass );
 			if ( mappedXClass == null ) {
 				continue;
@@ -367,7 +455,7 @@
 			);
 		}
 
-		bindFullTextFilterDef(defAnn);
+		bindFullTextFilterDef( defAnn );
 	}
 
 	private void bindFullTextFilterDef(FullTextFilterDef defAnn) {
@@ -419,10 +507,10 @@
 	private void initProgrammaticAnalyzers(ConfigContext context, ReflectionManager reflectionManager) {
 		final Map defaults = reflectionManager.getDefaults();
 
-		if (defaults != null) {
-			AnalyzerDef[] defs = (AnalyzerDef[]) defaults.get( AnalyzerDefs.class );
+		if ( defaults != null ) {
+			AnalyzerDef[] defs = ( AnalyzerDef[] ) defaults.get( AnalyzerDefs.class );
 			if ( defs != null ) {
-				for (AnalyzerDef def : defs) {
+				for ( AnalyzerDef def : defs ) {
 					context.addAnalyzerDef( def );
 				}
 			}
@@ -431,13 +519,13 @@
 
 	private void initProgrammaticallyDefinedFilterDef(ReflectionManager reflectionManager) {
 		@SuppressWarnings("unchecked") Map defaults = reflectionManager.getDefaults();
-		FullTextFilterDef[] filterDefs = (FullTextFilterDef[]) defaults.get( FullTextFilterDefs.class);
-		if (filterDefs != null && filterDefs.length != 0) {
-			for (FullTextFilterDef defAnn : filterDefs) {
+		FullTextFilterDef[] filterDefs = ( FullTextFilterDef[] ) defaults.get( FullTextFilterDefs.class );
+		if ( filterDefs != null && filterDefs.length != 0 ) {
+			for ( FullTextFilterDef defAnn : filterDefs ) {
 				if ( filterDefinitions.containsKey( defAnn.name() ) ) {
-					throw new SearchException("Multiple definition of @FullTextFilterDef.name=" + defAnn.name());
+					throw new SearchException( "Multiple definition of @FullTextFilterDef.name=" + defAnn.name() );
 				}
-				bindFullTextFilterDef(defAnn);
+				bindFullTextFilterDef( defAnn );
 			}
 		}
 	}
@@ -451,8 +539,10 @@
 			return new LogErrorHandler();
 		}
 		else {
-			return PluginLoader.instanceFromName( ErrorHandler.class, errorHandlerClassName,
-				ImmutableSearchFactory.class, "Error Handler" );
+			return PluginLoader.instanceFromName(
+					ErrorHandler.class, errorHandlerClassName,
+					ImmutableSearchFactory.class, "Error Handler"
+			);
 		}
 	}
 
@@ -534,7 +624,9 @@
 
 		public Similarity getSimilarity(DirectoryProvider<?> provider) {
 			Similarity similarity = dirProviderData.get( provider ).getSimilarity();
-			if ( similarity == null ) throw new SearchException( "Assertion error: a similarity should be defined for each provider" );
+			if ( similarity == null ) {
+				throw new SearchException( "Assertion error: a similarity should be defined for each provider" );
+			}
 			return similarity;
 		}
 
@@ -550,6 +642,6 @@
 		public <T> DocumentBuilderIndexedEntity<T> getDocumentBuilderIndexedEntity(Class<T> entityType) {
 			return ( DocumentBuilderIndexedEntity<T> ) documentBuildersIndexedEntities.get( entityType );
 		}
-		
+
 	}
 }

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfo.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfo.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfo.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,131 @@
+/* 
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.jmx;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+
+import org.hibernate.search.ProjectionConstants;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.reader.ReaderProvider;
+import org.hibernate.search.store.DirectoryProvider;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Implementation of the {@code HibernateSearchConfigInfoMBean} JMX attributes and operations.
+ *
+ * @author Hardy Ferentschik
+ */
+public class HibernateSearchConfigInfo implements HibernateSearchConfigInfoMBean {
+
+	private final SearchFactoryImplementor searchFactoryImplementor;
+
+	public HibernateSearchConfigInfo(SearchFactoryImplementor searchFactoryImplementor) {
+		this.searchFactoryImplementor = searchFactoryImplementor;
+	}
+
+	public Set<String> getIndexedClassNames() {
+		Set<String> indexedClasses = new HashSet<String>();
+		for ( Class clazz : searchFactoryImplementor.getDocumentBuildersIndexedEntities().keySet() ) {
+			indexedClasses.add( clazz.getName() );
+		}
+		return indexedClasses;
+	}
+
+	public String getIndexingStrategy() {
+		return searchFactoryImplementor.getIndexingStrategy();
+	}
+
+	public int getNumberOfIndexedEntities(String entity) {
+		Class<?> clazz = getEntityClass( entity );
+		DirectoryProvider[] directoryProviders = searchFactoryImplementor.getDirectoryProviders( clazz );
+		ReaderProvider readerProvider = searchFactoryImplementor.getReaderProvider();
+
+		int count = 0;
+		for ( DirectoryProvider directoryProvider : directoryProviders ) {
+			IndexReader reader = readerProvider.openReader( directoryProvider );
+			IndexSearcher searcher = new IndexSearcher( reader );
+			BooleanQuery boolQuery = new BooleanQuery();
+			boolQuery.add( new MatchAllDocsQuery(), BooleanClause.Occur.MUST );
+			boolQuery.add(
+					new TermQuery( new Term( ProjectionConstants.OBJECT_CLASS, entity ) ), BooleanClause.Occur.MUST
+			);
+			try {
+				TopDocs topdocs = searcher.search( boolQuery, 1 );
+				count += topdocs.totalHits;
+			}
+			catch ( IOException e ) {
+				throw new RuntimeException( "Unable to execute count query for entity " + entity, e );
+			}
+			finally {
+				readerProvider.closeReader( reader );
+			}
+		}
+		return count;
+	}
+
+	public Map<String, Integer> indexedEntitiesCount() {
+		Map<String, Integer> countPerEntity = new HashMap<String, Integer>();
+		for ( String className : getIndexedClassNames() ) {
+			countPerEntity.put( className, getNumberOfIndexedEntities( className ) );
+		}
+		return countPerEntity;
+	}
+
+	public List<String> getIndexingParameters(String entity) {
+		Class<?> clazz = getEntityClass( entity );
+		List<String> indexingParameters = new ArrayList<String>();
+		for ( DirectoryProvider directoryProvider : searchFactoryImplementor.getDirectoryProviders( clazz ) ) {
+			indexingParameters.add( searchFactoryImplementor.getIndexingParameters( directoryProvider ).toString() );
+		}
+		return indexingParameters;
+	}
+
+	private Class<?> getEntityClass(String entity) {
+		Class<?> clazz;
+		try {
+			clazz = ReflectHelper.classForName( entity, HibernateSearchConfigInfo.class );
+		}
+		catch ( ClassNotFoundException e ) {
+			throw new IllegalArgumentException( entity + "not a indexed entity" );
+		}
+		return clazz;
+	}
+}
+
+

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfoMBean.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfoMBean.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchConfigInfoMBean.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,83 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.jmx;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Defines the Hibernate Search exposed JMX attributes and operations for index configuration.
+ *
+ * @author Hardy Ferentschik
+ */
+public interface HibernateSearchConfigInfoMBean {
+
+	public static final String CONFIG_MBEAN_OBJECT_NAME = "org.hibernate.search.jmx:type=HibernateSearchConfigInfoMBean";
+
+	/**
+	 * Returns a list of all indexed classes.
+	 *
+	 * @return list of all indexed classes
+	 */
+	Set<String> getIndexedClassNames();
+
+	/**
+	 * Returns the indexing strategy - <i>manual</i> vs. event <i>event</i>.
+	 *
+	 * @return the indexing strategy
+	 */
+	String getIndexingStrategy();
+
+	/**
+	 * Returns the number of documents for the given entity.
+	 *
+	 * @param entity the fqc of the entity
+	 *
+	 * @return number of documents for the specified entity name
+	 *
+	 * @throws IllegalArgumentException in case the entity name is not valid
+	 */
+	int getNumberOfIndexedEntities(String entity);
+
+	/**
+	 * A list of string representations of the indexing parameters for each directory of the specified entity.
+	 * Defaults are not displayed, but only parameters which are explicitly set via the configuration.
+	 *
+	 * @param entity the fqc of the entity
+	 *
+	 * @return A list of string representations of the indexing parameters for each directory of the specified entity
+	 *
+	 * @throws IllegalArgumentException in case the entity name is not valid
+	 */
+	List<String> getIndexingParameters(String entity);
+
+	/**
+	 * Returns a map of all indexed entities and their document count in the index.
+	 *
+	 * @return a map of all indexed entities and their document count. The map key is the fqc of the entity and
+	 *         the map value is the document count.
+	 */
+	Map<String, Integer> indexedEntitiesCount();
+}

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrl.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrl.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrl.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,115 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.jmx;
+
+import java.util.Properties;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.util.JNDIHelper;
+import org.hibernate.util.ReflectHelper;
+
+
+/**
+ * Implementation of the {@code HibernateSearchIndexCtrlMBean} JMX attributes and operations.
+ *
+ * @author Hardy Ferentschik
+ */
+public class HibernateSearchIndexCtrl implements HibernateSearchIndexCtrlMBean {
+	private static final String HIBERNATE_JNDI_PREFIX = "hibernate.jndi.";
+
+	private final Properties jndiProperties;
+	private final String sessionFactoryJndiName;
+
+	public HibernateSearchIndexCtrl(Properties props) {
+		this.sessionFactoryJndiName = props.getProperty( "hibernate.session_factory_name" );
+		this.jndiProperties = JNDIHelper.getJndiProperties( props, HIBERNATE_JNDI_PREFIX );
+	}
+
+	public void index(String entity) {
+		Class<?> clazz = getEntityClass( entity );
+
+		SessionFactory factory = getSessionFactory();
+		Session session = factory.openSession();
+		FullTextSession fulltextSession = Search.getFullTextSession( session );
+		try {
+			fulltextSession.createIndexer( clazz )
+					.batchSizeToLoadObjects( 25 )
+					.cacheMode( CacheMode.NORMAL )
+					.threadsToLoadObjects( 5 )
+					.threadsForSubsequentFetching( 20 )
+					.startAndWait();
+		}
+		catch ( InterruptedException e ) {
+			throw new RuntimeException( "Unable to complete indexing" );
+		}
+		session.close();
+	}
+
+	public void purge(String entity) {
+		Class<?> clazz = getEntityClass( entity );
+
+		SessionFactory factory = getSessionFactory();
+		Session session = factory.openSession();
+		FullTextSession fullTextSession = Search.getFullTextSession( session );
+		fullTextSession.beginTransaction();
+		fullTextSession.purgeAll( clazz );
+		fullTextSession.getTransaction().commit();
+		session.close();
+	}
+
+	private Class<?> getEntityClass(String entity) {
+		Class<?> clazz;
+		try {
+			clazz = ReflectHelper.classForName( entity, HibernateSearchIndexCtrl.class );
+		}
+		catch ( ClassNotFoundException e ) {
+			throw new IllegalArgumentException( entity + "not a indexed entity" );
+		}
+		return clazz;
+	}
+
+	private SessionFactory getSessionFactory() {
+		try {
+			Context initialContext;
+			if ( jndiProperties.isEmpty() ) {
+				initialContext = new InitialContext();
+			}
+			else {
+				initialContext = new InitialContext( jndiProperties );
+			}
+			return ( SessionFactory ) initialContext.lookup( sessionFactoryJndiName );
+		}
+		catch ( Exception e ) {
+			throw new UnsupportedOperationException(
+					"In order for this operation to work the SessionFactory must be bound to JNDI"
+			);
+		}
+	}
+}
\ No newline at end of file

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrlMBean.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrlMBean.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/jmx/HibernateSearchIndexCtrlMBean.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,62 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.jmx;
+
+/**
+ * Defines the Hibernate Search exposed JMX attributes and operations for index creation and purging.
+ *
+ * @author Hardy Ferentschik
+ */
+public interface HibernateSearchIndexCtrlMBean {
+
+	public static final String INDEX_CTRL_MBEAN_OBJECT_NAME = "org.hibernate.search.jmx:type=HibernateSearchIndexCtrlMBean";
+
+	/**
+	 * Index the specified entity using the mass indexer.
+	 * <p><b>Note:<br/>
+	 * This method is only available if the Hibernate {@code SessionFactory}
+	 * is available via JNDI.
+	 * </p>
+	 *
+	 * @param entity The fqc of the entity to index
+	 *
+	 * @throws IllegalArgumentException	  in case the entity name is not valid
+	 * @throws UnsupportedOperationException in case the Hibernate {@code SessionFactory} is not bound via JNDI.
+	 */
+	void index(String entity);
+
+	/**
+	 * Purge the index of the specified entity.
+	 * <p><b>Note:<br/>
+	 * This method is only available if the Hibernate {@code SessionFactory}
+	 * is available via JNDI.
+	 * </p>
+	 *
+	 * @param entity The fqc of the entity to index
+	 *
+	 * @throws IllegalArgumentException	  in case the entity name is not valid
+	 * @throws UnsupportedOperationException in case the Hibernate {@code SessionFactory} is not bound via JNDI.
+	 */
+	void purge(String entity);
+}
\ No newline at end of file

Added: search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/JNDIHelper.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/JNDIHelper.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/JNDIHelper.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,112 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.util;
+
+import java.util.HashSet;
+import java.util.Properties;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Helper class for creating an JNDI {@code InitialContext}.
+ *
+ * @author Hardy Ferentschik
+ */
+public class JNDIHelper {
+	private JNDIHelper() {
+	}
+
+	/**
+	 * Creates an initial context
+	 *
+	 * @param properties Configuration properties to check for JNDI specific properties
+	 * @param prefix The prefix used to designate JDNI properties. If a property from {@code property} contains
+	 * a property which matches the prefix, the prefix gets removed and the property passed to the initial context creation.
+	 *
+	 * @return the initial context
+	 *
+	 * @throws NamingException in case an error occurs creating the {@code InitialContext}
+	 */
+	public static InitialContext getInitialContext(Properties properties, String prefix) throws NamingException {
+		Properties jndiProps = getJndiProperties( properties, prefix );
+		if ( jndiProps.size() == 0 ) {
+			return new InitialContext();
+		}
+		else {
+			return new InitialContext( jndiProps );
+		}
+	}
+
+	public static Properties getJndiProperties(Properties properties, String prefix) {
+
+		HashSet<String> specialProps = new HashSet<String>();
+		specialProps.add( prefix + "class" );
+		specialProps.add( prefix + "url" );
+
+		Properties result = addJNDIProperties( properties, prefix, specialProps );
+
+		handleSpecialPropertyTranslation( properties, prefix + "class", result, Context.INITIAL_CONTEXT_FACTORY );
+		handleSpecialPropertyTranslation( properties, prefix + "url", result, Context.PROVIDER_URL );
+
+		return result;
+	}
+
+	/**
+	 * Creates a new {@code Properties} instance with all properties from {@code properties} which start with the given
+	 *
+	 * @param properties the original properties
+	 * @param prefix the prefix indicating JNDI specific properties
+	 * @param specialProps a set of property names to ignore
+	 *
+	 * @return Creates a new {@code Properties} instance with JNDI specific properties
+	 *
+	 * @{code prefix}. In the new instance the prefix is removed. If a property matches a value in {@code specialProps}
+	 * it gets ignored.
+	 */
+	private static Properties addJNDIProperties(Properties properties, String prefix, HashSet<String> specialProps) {
+		Properties result = new Properties();
+		for ( Object property : properties.keySet() ) {
+			if ( property instanceof String ) {
+				String s = ( String ) property;
+				if ( s.indexOf( prefix ) > -1 && !specialProps.contains( s ) ) {
+					result.setProperty( s.substring( prefix.length() ), properties.getProperty( s ) );
+				}
+			}
+		}
+		return result;
+	}
+
+	private static void handleSpecialPropertyTranslation(Properties originalProperties, String oldKey, Properties newProperties, String newKey) {
+		String value = originalProperties.getProperty( oldKey );
+		// we want to be able to just use the defaults,
+		// if JNDI environment properties are not supplied
+		// so don't put null in anywhere
+		if ( value != null ) {
+			newProperties.put( newKey, value );
+		}
+	}
+}
+
+

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/SearchTestCase.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/SearchTestCase.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/SearchTestCase.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -26,6 +26,7 @@
 
 import java.io.File;
 import java.io.InputStream;
+import java.net.URL;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.KeywordAnalyzer;
@@ -62,14 +63,14 @@
 public abstract class SearchTestCase extends TestCase {
 
 	private static final Logger log = org.hibernate.search.util.LoggerFactory.make();
-	
+
 	public static final Analyzer standardAnalyzer = new StandardAnalyzer( getTargetLuceneVersion() );
 	public static final Analyzer stopAnalyzer = new StopAnalyzer( getTargetLuceneVersion() );
 	public static final Analyzer simpleAnalyzer = new SimpleAnalyzer();
 	public static final Analyzer keywordAnalyzer = new KeywordAnalyzer();
 
 	private static File indexDir;
-	
+
 	private SearchFactoryImplementor searchFactory;
 
 	static {
@@ -134,12 +135,12 @@
 		tx.commit();
 		s.close();
 	}
-	
+
 	protected SearchFactory getSearchFactory() {
 		if ( searchFactory == null ) {
 			Session session = openSession();
 			FullTextSession fullTextSession = Search.getFullTextSession( session );
-			searchFactory = ( SearchFactoryImplementor) fullTextSession.getSearchFactory();
+			searchFactory = ( SearchFactoryImplementor ) fullTextSession.getSearchFactory();
 			fullTextSession.close();
 		}
 		return searchFactory;
@@ -195,9 +196,28 @@
 	protected static File getIndexDir() {
 		return indexDir;
 	}
-	
+
 	public static Version getTargetLuceneVersion() {
 		return Version.LUCENE_29;
 	}
-	
+
+	/**
+	 * Returns the target directory of the build.
+	 *
+	 * @return the target directory of the build
+	 */
+	public File getTargetDir() {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		// get a URL reference to something we now is part of the classpath (us)
+		URL myUrl = contextClassLoader.getResource( SearchTestCase.class.getName().replace( '.', '/' ) + ".class" );
+		File myPath = new File( myUrl.getFile() );
+		// navigate back to '/target'
+		return myPath
+				.getParentFile()  // target/classes/org/hibernate/search/test
+				.getParentFile()  // target/classes/org/hibernate/search
+				.getParentFile()  // target/classes/org/hibernate/
+				.getParentFile()  // target/classes/org
+				.getParentFile()  // target/classes/
+				.getParentFile(); // target
+	}
 }

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/master/JMSMasterTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/master/JMSMasterTest.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/master/JMSMasterTest.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -29,6 +29,7 @@
 import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
 import javax.jms.MessageConsumer;
 import javax.jms.ObjectMessage;
 import javax.jms.Queue;
@@ -37,12 +38,14 @@
 import javax.jms.QueueSender;
 import javax.jms.QueueSession;
 import javax.naming.Context;
+import javax.naming.NamingException;
 
 import org.apache.activemq.broker.BrokerService;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.queryParser.QueryParser;
 import org.apache.lucene.search.Query;
+
 import org.hibernate.Session;
 import org.hibernate.cfg.Configuration;
 import org.hibernate.search.Environment;
@@ -114,13 +117,13 @@
 	}
 
 	private Queue getMessageQueue() throws Exception {
-		Context ctx = new javax.naming.InitialContext();
+		Context ctx = getJndiInitialContext();
 		return ( Queue ) ctx.lookup( QUEUE_NAME );
 	}
 
 	private QueueSession getQueueSession() throws Exception {
 		if ( queueSession == null ) {
-			Context ctx = new javax.naming.InitialContext();
+			Context ctx = getJndiInitialContext();
 			QueueConnectionFactory factory = ( QueueConnectionFactory ) ctx.lookup( CONNECTION_FACTORY_NAME );
 			QueueConnection conn = factory.createQueueConnection();
 			conn.start();
@@ -130,6 +133,18 @@
 		return queueSession;
 	}
 
+	private Context getJndiInitialContext() throws NamingException {
+		Properties props = new Properties();
+		props.setProperty(
+				Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory"
+		);
+		props.setProperty( Context.PROVIDER_URL, "vm://localhost" );
+		props.setProperty( "connectionFactoryNames", "ConnectionFactory, java:/ConnectionFactory" );
+		props.setProperty( "queue.queue/searchtest", "searchQueue" );
+		Context ctx = new javax.naming.InitialContext( props );
+		return ctx;
+	}
+
 	/**
 	 * Manually create the work queue. This lists gets send by the Slaves to the Master for indexing.
 	 *
@@ -194,7 +209,7 @@
 
 	protected void configure(Configuration cfg) {
 		super.configure( cfg );
-		// explcitily set the backend even though lucene is default.
+		// explicitly set the backend even though lucene is default.
 		cfg.setProperty( Environment.WORKER_BACKEND, "lucene" );
 	}
 

Modified: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/slave/JMSSlaveTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/slave/JMSSlaveTest.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jms/slave/JMSSlaveTest.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -24,21 +24,23 @@
  */
 package org.hibernate.search.test.jms.slave;
 
+import java.util.Properties;
 import javax.jms.MessageConsumer;
 import javax.jms.Queue;
 import javax.jms.QueueConnection;
 import javax.jms.QueueConnectionFactory;
 import javax.jms.QueueSession;
 import javax.naming.Context;
+import javax.naming.NamingException;
 
 import org.apache.activemq.broker.BrokerService;
 
+import org.hibernate.Session;
+import org.hibernate.Transaction;
 import org.hibernate.cfg.Configuration;
 import org.hibernate.search.Environment;
 import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
 import org.hibernate.search.test.SearchTestCase;
-import org.hibernate.Session;
-import org.hibernate.Transaction;
 
 /**
  * Checks that the Slave in a JMS configuration proplerly places index jobs onto the queue.
@@ -68,7 +70,7 @@
 	public void testMessageSend() throws Exception {
 		registerMessageListener();
 		SearchQueueChecker.reset();
-		
+
 		Session s = openSession();
 		Transaction tx = s.beginTransaction();
 		TShirt ts = new TShirt();
@@ -82,7 +84,7 @@
 		tx.commit();
 
 		//need to sleep for the message consumption
-		Thread.sleep(500);
+		Thread.sleep( 500 );
 
 		assertEquals( 1, SearchQueueChecker.queues );
 		assertEquals( 2, SearchQueueChecker.works );
@@ -90,12 +92,12 @@
 		SearchQueueChecker.reset();
 		s = openSession();
 		tx = s.beginTransaction();
-		ts = (TShirt) s.get( TShirt.class, ts.getId() );
+		ts = ( TShirt ) s.get( TShirt.class, ts.getId() );
 		ts.setLogo( "Peter pan" );
 		tx.commit();
 
 		//need to sleep for the message consumption
-		Thread.sleep(500);
+		Thread.sleep( 500 );
 
 		assertEquals( 1, SearchQueueChecker.queues );
 		assertEquals( 2, SearchQueueChecker.works ); //one update = 2 works
@@ -108,8 +110,8 @@
 		tx.commit();
 
 		//Need to sleep for the message consumption
-		Thread.sleep(500);
-		
+		Thread.sleep( 500 );
+
 		assertEquals( 1, SearchQueueChecker.queues );
 		assertEquals( 2, SearchQueueChecker.works );
 		s.close();
@@ -137,13 +139,13 @@
 	}
 
 	private Queue getMessageQueue() throws Exception {
-		Context ctx = new javax.naming.InitialContext();
+		Context ctx = getJndiInitialContext();
 		return ( Queue ) ctx.lookup( QUEUE_NAME );
 	}
 
 	private QueueSession getQueueSession() throws Exception {
 		if ( queueSession == null ) {
-			Context ctx = new javax.naming.InitialContext();
+			Context ctx = getJndiInitialContext();
 			QueueConnectionFactory factory = ( QueueConnectionFactory ) ctx.lookup( CONNECTION_FACTORY_NAME );
 			QueueConnection conn = factory.createQueueConnection();
 			conn.start();
@@ -158,6 +160,15 @@
 		cfg.setProperty( Environment.WORKER_BACKEND, "jms" );
 		cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_CONNECTION_FACTORY, CONNECTION_FACTORY_NAME );
 		cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_QUEUE, QUEUE_NAME );
+
+		// use the hibernate.search.worker.jndi prefix to pass a whole bunch of jndi properties to create the InitialContext
+		// for the queue processor
+		cfg.setProperty(
+				"hibernate.search.worker.jndi.class", "org.apache.activemq.jndi.ActiveMQInitialContextFactory"
+		);
+		cfg.setProperty( "hibernate.search.worker.jndi.url", "vm://localhost" );
+		cfg.setProperty( "hibernate.search.worker.jndi.connectionFactoryNames", "ConnectionFactory, java:/ConnectionFactory" );
+		cfg.setProperty( "hibernate.search.worker.jndi.queue.queue/searchtest", "searchQueue" );
 	}
 
 	protected Class<?>[] getMappings() {
@@ -165,4 +176,17 @@
 				TShirt.class
 		};
 	}
+
+	private Context getJndiInitialContext() throws NamingException {
+		Properties props = new Properties();
+		props.setProperty(
+				Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory"
+		);
+		props.setProperty( Context.PROVIDER_URL, "vm://localhost" );
+		props.setProperty( "connectionFactoryNames", "ConnectionFactory, java:/ConnectionFactory" );
+		props.setProperty( "queue.queue/searchtest", "searchQueue" );
+
+		Context ctx = new javax.naming.InitialContext( props );
+		return ctx;
+	}
 }

Added: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/ConfigInfoMBeanTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/ConfigInfoMBeanTest.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/ConfigInfoMBeanTest.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,147 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.test.jmx;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashSet;
+import java.util.Set;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.search.Environment;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.jmx.HibernateSearchConfigInfoMBean;
+import org.hibernate.search.test.SearchTestCase;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class ConfigInfoMBeanTest extends SearchTestCase {
+	MBeanServer mbeanServer;
+	ObjectName configBeanObjectName;
+
+	public void testConfigInfoMBeanRegistered() throws Exception {
+		assertTrue(
+				"With the right property set the Search MBean should be registered",
+				mbeanServer.isRegistered( configBeanObjectName )
+		);
+	}
+
+	public void testAttributesAndOperations() throws Exception {
+		MBeanInfo info = mbeanServer.getMBeanInfo( configBeanObjectName );
+		MBeanAttributeInfo[] attributes = info.getAttributes();
+		assertEquals( "Wrong number of attributes", 2, attributes.length );
+		Set<String> attributeNames = new HashSet<String>();
+		attributeNames.add( "IndexedClassNames" );
+		attributeNames.add( "IndexingStrategy" );
+		for ( MBeanAttributeInfo attribute : attributes ) {
+			assertTrue( attributeNames.contains( attribute.getName() ) );
+		}
+
+		MBeanOperationInfo[] operations = info.getOperations();
+		assertEquals( "Wrong number of operations", 3, operations.length );
+		Set<String> operationNames = new HashSet<String>();
+		operationNames.add( "getNumberOfIndexedEntities" );
+		operationNames.add( "indexedEntitiesCount" );
+		operationNames.add( "getIndexingParameters" );
+		for ( MBeanOperationInfo operation : operations ) {
+			assertTrue( operationNames.contains( operation.getName() ) );
+		}
+	}
+
+	public void testIndexingStrategy() throws Exception {
+		assertEquals(
+				"wrong even type", "event", mbeanServer.getAttribute( configBeanObjectName, "IndexingStrategy" )
+		);
+	}
+
+	public void testIndexedClassNames() throws Exception {
+		assertEquals(
+				"wrong class name",
+				Counter.class.getName(),
+				( ( Set ) mbeanServer.getAttribute( configBeanObjectName, "IndexedClassNames" ) ).iterator().next()
+		);
+	}
+
+
+	public void testNumberOfIndexedEntities() throws Exception {
+		assertNumberOfIndexedEntities( Counter.class.getName(), 0 );
+
+		FullTextSession s = Search.getFullTextSession( openSession() );
+		Transaction tx = s.beginTransaction();
+		Counter counter = new Counter();
+		s.save( counter );
+		tx.commit();
+		s.close();
+
+		assertNumberOfIndexedEntities( Counter.class.getName(), 1 );
+	}
+
+	private void assertNumberOfIndexedEntities(String entity, int count)
+			throws InstanceNotFoundException, MBeanException, ReflectionException {
+		assertEquals(
+				"wrong number of indexed entities", count,
+				mbeanServer.invoke(
+						configBeanObjectName,
+						"getNumberOfIndexedEntities",
+						new String[] { entity },
+						new String[] { String.class.getName() }
+				)
+		);
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		mbeanServer = ManagementFactory.getPlatformMBeanServer();
+		configBeanObjectName = new ObjectName( HibernateSearchConfigInfoMBean.CONFIG_MBEAN_OBJECT_NAME );
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		if ( mbeanServer.isRegistered( configBeanObjectName ) ) {
+			mbeanServer.unregisterMBean( configBeanObjectName );
+		}
+	}
+
+	protected void configure(Configuration cfg) {
+		super.configure( cfg );
+		cfg.setProperty( Environment.JMX_ENABLED, "true" );
+	}
+
+	@Override
+	protected Class<?>[] getMappings() {
+		return new Class<?>[] { Counter.class };
+	}
+}
+
+

Added: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/Counter.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/Counter.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/Counter.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,47 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.test.jmx;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.hibernate.search.annotations.Indexed;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+ at Indexed
+public class Counter {
+	@Id
+	@GeneratedValue
+	private long id;
+
+	public long getId() {
+		return id;
+	}
+}
+
+

Added: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/IndexCtrlMBeanTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/IndexCtrlMBeanTest.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/IndexCtrlMBeanTest.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,160 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.test.jmx;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+import java.util.HashSet;
+import java.util.Set;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.search.Environment;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.jmx.HibernateSearchConfigInfoMBean;
+import org.hibernate.search.jmx.HibernateSearchIndexCtrlMBean;
+import org.hibernate.search.test.SearchTestCase;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class IndexCtrlMBeanTest extends SearchTestCase {
+	MBeanServer mbeanServer;
+	ObjectName configBeanObjectName;
+	ObjectName indexBeanObjectName;
+
+	public void testIndexCtrlMBeanRegistered() throws Exception {
+		assertTrue(
+				"With the right property set the Search MBean should be registered",
+				mbeanServer.isRegistered( indexBeanObjectName )
+		);
+	}
+
+	public void testAttributesAndOperations() throws Exception {
+		MBeanInfo info = mbeanServer.getMBeanInfo( indexBeanObjectName );
+		MBeanAttributeInfo[] attributes = info.getAttributes();
+		assertEquals( "Wrong number of attributes", 0, attributes.length );
+
+		MBeanOperationInfo[] operations = info.getOperations();
+		assertEquals( "Wrong number of operations", 2, operations.length );
+		Set<String> operationNames = new HashSet<String>();
+		operationNames.add( "index" );
+		operationNames.add( "purge" );
+		for ( MBeanOperationInfo operation : operations ) {
+			assertTrue( operationNames.contains( operation.getName() ) );
+		}
+	}
+
+	public void testIndexAndPurge() throws Exception {
+		assertEquals(
+				"wrong even type", "manual", mbeanServer.getAttribute( configBeanObjectName, "IndexingStrategy" )
+		);
+
+		FullTextSession s = Search.getFullTextSession( openSession() );
+		Transaction tx = s.beginTransaction();
+		Counter counter = new Counter();
+		s.save( counter );
+		tx.commit();
+		s.close();
+
+		assertNumberOfIndexedEntities( Counter.class.getName(), 0 ); // manual indexing!
+
+		mbeanServer.invoke(
+				indexBeanObjectName,
+				"index",
+				new String[] { Counter.class.getName() },
+				new String[] { String.class.getName() }
+		);
+
+		assertNumberOfIndexedEntities( Counter.class.getName(), 1 );
+
+		mbeanServer.invoke(
+				indexBeanObjectName,
+				"purge",
+				new String[] { Counter.class.getName() },
+				new String[] { String.class.getName() }
+		);
+
+		assertNumberOfIndexedEntities( Counter.class.getName(), 0 );
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		mbeanServer = ManagementFactory.getPlatformMBeanServer();
+		configBeanObjectName = new ObjectName( HibernateSearchConfigInfoMBean.CONFIG_MBEAN_OBJECT_NAME );
+		indexBeanObjectName = new ObjectName( HibernateSearchIndexCtrlMBean.INDEX_CTRL_MBEAN_OBJECT_NAME );
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		if ( mbeanServer.isRegistered( configBeanObjectName ) ) {
+			mbeanServer.unregisterMBean( configBeanObjectName );
+		}
+		if ( mbeanServer.isRegistered( indexBeanObjectName ) ) {
+			mbeanServer.unregisterMBean( indexBeanObjectName );
+		}
+	}
+
+	protected void configure(Configuration cfg) {
+		super.configure( cfg );
+		File targetDir = getTargetDir();
+		File simpleJndiDir = new File( targetDir, "simpleJndi" );
+		simpleJndiDir.mkdir();
+
+		cfg.setProperty( "hibernate.session_factory_name", "java:comp/SessionFactory" );
+		cfg.setProperty( "hibernate.jndi.class", "org.osjava.sj.SimpleContextFactory" );
+		cfg.setProperty( "hibernate.jndi.org.osjava.sj.root", simpleJndiDir.getAbsolutePath() );
+		cfg.setProperty( "hibernate.jndi.org.osjava.sj.jndi.shared", "true" );
+
+		cfg.setProperty( "hibernate.search.indexing_strategy", "manual" );
+		cfg.setProperty( Environment.JMX_ENABLED, "true" );
+	}
+
+	@Override
+	protected Class<?>[] getMappings() {
+		return new Class<?>[] { Counter.class };
+	}
+
+	private void assertNumberOfIndexedEntities(String entity, int count)
+			throws InstanceNotFoundException, MBeanException, ReflectionException {
+		assertEquals(
+				"wrong number of indexed entities", count,
+				mbeanServer.invoke(
+						configBeanObjectName,
+						"getNumberOfIndexedEntities",
+						new String[] { entity },
+						new String[] { String.class.getName() }
+				)
+		);
+	}
+}
\ No newline at end of file

Added: search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/NoMBeansEnabledTest.java
===================================================================
--- search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/NoMBeansEnabledTest.java	                        (rev 0)
+++ search/trunk/hibernate-search/src/test/java/org/hibernate/search/test/jmx/NoMBeansEnabledTest.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -0,0 +1,75 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ *  Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
+ *  indicated by the @author tags or express copyright attribution
+ *  statements applied by the authors.  All third-party contributions are
+ *  distributed under license by Red Hat, Inc.
+ *
+ *  This copyrighted material is made available to anyone wishing to use, modify,
+ *  copy, or redistribute it subject to the terms and conditions of the GNU
+ *  Lesser General Public License, as published by the Free Software Foundation.
+ *
+ *  This program 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 Lesser General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this distribution; if not, write to:
+ *  Free Software Foundation, Inc.
+ *  51 Franklin Street, Fifth Floor
+ *  Boston, MA  02110-1301  USA
+ */
+package org.hibernate.search.test.jmx;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.search.Environment;
+import org.hibernate.search.jmx.HibernateSearchConfigInfoMBean;
+import org.hibernate.search.jmx.HibernateSearchIndexCtrlMBean;
+import org.hibernate.search.test.SearchTestCase;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class NoMBeansEnabledTest extends SearchTestCase {
+
+	public void testMBeanNotRegisteredWithoutExplicitProperty() throws Exception {
+		MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+		ObjectName name = new ObjectName( HibernateSearchConfigInfoMBean.CONFIG_MBEAN_OBJECT_NAME );
+		assertFalse(
+				"Without '" + Environment.JMX_ENABLED + "' set the configuration info MBean should not be registered",
+				mbs.isRegistered( name )
+		);
+
+		name = new ObjectName( HibernateSearchIndexCtrlMBean.INDEX_CTRL_MBEAN_OBJECT_NAME );
+		assertFalse(
+				"Without '" + Environment.JMX_ENABLED + "' set the index control MBean should not be registered",
+				mbs.isRegistered( name )
+		);
+	}
+
+
+	protected void configure(Configuration cfg) {
+		super.configure( cfg );
+		File targetDir = getTargetDir();
+		File simpleJndiDir = new File( targetDir, "simpleJndi" );
+		simpleJndiDir.mkdir();
+
+		cfg.setProperty( "hibernate.session_factory_name", "java:comp/SessionFactory" );
+		cfg.setProperty( "hibernate.jndi.class", "org.osjava.sj.SimpleContextFactory" );
+		cfg.setProperty( "hibernate.jndi.org.osjava.sj.root", simpleJndiDir.getAbsolutePath() );
+		cfg.setProperty( "hibernate.jndi.org.osjava.sj.jndi.shared", "true" );
+	}
+
+	@Override
+	protected Class<?>[] getMappings() {
+		return new Class<?>[] { Counter.class };
+	}
+}
\ No newline at end of file

Deleted: search/trunk/hibernate-search/src/test/resources/jndi.properties
===================================================================
--- search/trunk/hibernate-search/src/test/resources/jndi.properties	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search/src/test/resources/jndi.properties	2010-07-05 14:46:01 UTC (rev 19897)
@@ -1,12 +0,0 @@
-java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
-java.naming.provider.url=vm://localhost
-
-# use the following property to specify the JNDI name the connection factory
-# should appear as. 
-connectionFactoryNames = ConnectionFactory, java:/ConnectionFactory
-
-# register some queues in JNDI using the form
-# queue.[jndiName] = [physicalName]
-queue.queue/searchtest = searchQueue
-
-

Modified: search/trunk/hibernate-search-testing/src/main/java/org/hibernate/search/test/SearchTestCase.java
===================================================================
--- search/trunk/hibernate-search-testing/src/main/java/org/hibernate/search/test/SearchTestCase.java	2010-07-05 14:30:38 UTC (rev 19896)
+++ search/trunk/hibernate-search-testing/src/main/java/org/hibernate/search/test/SearchTestCase.java	2010-07-05 14:46:01 UTC (rev 19897)
@@ -26,6 +26,7 @@
 
 import java.io.File;
 import java.io.InputStream;
+import java.net.URL;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.KeywordAnalyzer;
@@ -61,14 +62,14 @@
 public abstract class SearchTestCase extends TestCase {
 
 	private static final Logger log = org.hibernate.search.util.LoggerFactory.make();
-	
+
 	public static Analyzer standardAnalyzer = new StandardAnalyzer( getTargetLuceneVersion() );
 	public static Analyzer stopAnalyzer = new StopAnalyzer( getTargetLuceneVersion() );
 	public static Analyzer simpleAnalyzer = new SimpleAnalyzer();
 	public static Analyzer keywordAnalyzer = new KeywordAnalyzer();
 
 	private static File indexDir;
-	
+
 	private SearchFactory searchFactory;
 
 	static {
@@ -129,7 +130,7 @@
 		tx.commit();
 		s.close();
 	}
-	
+
 	protected SearchFactory getSearchFactory() {
 		if ( searchFactory == null ) {
 			Session session = openSession();
@@ -190,9 +191,29 @@
 	protected static File getIndexDir() {
 		return indexDir;
 	}
-	
+
 	public static Version getTargetLuceneVersion() {
 		return Version.LUCENE_29;
 	}
-	
+
+	/**
+	 * Returns the target directory of the build.
+	 *
+	 * @return the target directory of the build
+	 */
+	public File getTargetDir() {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		// get a URL reference to something we now is part of the classpath (us)
+		URL myUrl = contextClassLoader.getResource( SearchTestCase.class.getName().replace( '.', '/' ) + ".class" );
+		File myPath = new File( myUrl.getFile() );
+		// navigate back to '/target'
+		return myPath
+				.getParentFile()  // target/classes/org/hibernate/search/test
+				.getParentFile()  // target/classes/org/hibernate/search
+				.getParentFile()  // target/classes/org/hibernate/
+				.getParentFile()  // target/classes/org
+				.getParentFile()  // target/classes/
+				.getParentFile(); // target
+	}
+
 }



More information about the hibernate-commits mailing list