/* * 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.jgroups.master; import java.util.ArrayList; import java.util.List; import java.util.UUID; import junit.framework.Assert; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.NumericField; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.Query; import org.jgroups.JChannel; import org.jgroups.Message; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import org.hibernate.search.Environment; import org.hibernate.search.FullTextSession; import org.hibernate.search.ProjectionConstants; import org.hibernate.search.Search; import org.hibernate.search.backend.AddLuceneWork; import org.hibernate.search.backend.LuceneWork; import org.hibernate.search.backend.impl.jgroups.JGroupsChannelProvider; import org.hibernate.search.backend.impl.jgroups.MessageSerializationHelper; import org.hibernate.search.indexes.spi.IndexManager; import org.hibernate.search.test.SearchTestCase; import org.hibernate.search.test.TestConstants; import org.hibernate.search.test.jgroups.common.JGroupsCommonTest; import org.hibernate.search.test.jms.master.TShirt; import org.hibernate.search.util.configuration.impl.ConfigurationParseHelper; /** * Tests that the Master node in a JGroups cluster can properly process messages received from channel. * <p/> * In case of running test outside Hibernate Search Maven configuration set following VM configuration: * <br><br> * <code> * -Djava.net.preferIPv4Stack=true -Djgroups.bind_addr=127.0.0.1 * </code> * * @author Lukasz Moren * @author Sanne Grinovero <sanne@hibernate.org> (C) 2011 Red Hat Inc. */ public class JGroupsMasterTest extends SearchTestCase { private final QueryParser parser = new QueryParser( TestConstants.getTargetLuceneVersion(), "id", TestConstants.stopAnalyzer ); /** * Name of the JGroups channel used in test */ public static final String CHANNEL_NAME = UUID.randomUUID().toString(); private JChannel channel; public void testMessageSending() throws Exception { assertEquals( 0, countByQuery( "logo:jboss" ) ); TShirt shirt = createObjectWithSQL(); List<LuceneWork> queue = createDocumentAndWorkQueue( shirt ); sendMessage( queue ); boolean failed = true; for ( int i = 0; i < JGroupsCommonTest.MAX_WAITS; i++ ) { Thread.sleep( JGroupsCommonTest.NETWORK_WAIT_MILLISECONDS ); if ( countByQuery( "logo:jboss" ) == 1 ) { //the condition we're waiting for failed = false; break; //enough time wasted } } if ( failed ) Assert.fail( "Message not received after waiting for long!" ); } private int countByQuery(String luceneQueryString) throws ParseException { FullTextSession ftSess = Search.getFullTextSession( openSession() ); try { ftSess.getTransaction().begin(); try { Query luceneQuery = parser.parse( luceneQueryString ); org.hibernate.Query query = ftSess.createFullTextQuery( luceneQuery ); List result = query.list(); return result.size(); } finally { ftSess.getTransaction().commit(); } } finally { ftSess.close(); } } private void prepareJGroupsChannel() throws Exception { channel = new JChannel( ConfigurationParseHelper.locateConfig( "testing-flush-loopback.xml" ) ); channel.connect( CHANNEL_NAME ); } private void sendMessage(List<LuceneWork> queue) throws Exception { final String indexManagerName = "org.hibernate.search.test.jms.master.TShirt"; IndexManager indexManager = getSearchFactoryImpl().getAllIndexesManager().getIndexManager( indexManagerName ); //send message to all listeners byte[] data = indexManager.getSerializer().toSerializedModel( queue ); data = MessageSerializationHelper.prependString( indexManagerName, data ); Message message = new Message( null, null, data ); channel.send( message ); } /** * Manually create the work queue. This lists gets send by the Slaves to the Master for indexing. * * @param shirt The shirt to index * * @return A manually create <code>LuceneWork</code> list. */ private List<LuceneWork> createDocumentAndWorkQueue(TShirt shirt) { Document doc = new Document(); Field field = new Field( ProjectionConstants.OBJECT_CLASS, shirt.getClass().getName(), Field.Store.YES, Field.Index.NOT_ANALYZED ); doc.add( field ); field = new Field( "id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED ); doc.add( field ); field = new Field( "logo", shirt.getLogo(), Field.Store.NO, Field.Index.ANALYZED ); doc.add( field ); NumericField numField = new NumericField( "length" ); numField.setDoubleValue( shirt.getLength() ); doc.add( numField ); LuceneWork luceneWork = new AddLuceneWork( shirt.getId(), String.valueOf( shirt.getId() ), shirt.getClass(), doc ); List<LuceneWork> queue = new ArrayList<LuceneWork>(); queue.add( luceneWork ); return queue; } /** * Create a test object and delete if from index. * * @return a <code>TShirt</code> test object. */ private TShirt createObjectWithSQL() { Session s = openSession(); s.getTransaction().begin(); TShirt ts = new TShirt(); ts.setId( 1 ); ts.setLogo( "JBoss balls" ); ts.setSize( "large" ); ts.setLength( 23.2d ); s.persist( ts ); s.getTransaction().commit(); FullTextSession fullTextSession = Search.getFullTextSession( s ); fullTextSession.beginTransaction(); fullTextSession.purge( TShirt.class, 1 ); fullTextSession.getTransaction().commit(); s.close(); return ts; } @Override public void setUp() throws Exception { prepareJGroupsChannel(); super.setUp(); } @Override public void tearDown() throws Exception { channel.close(); super.tearDown(); } protected void configure(Configuration cfg) { super.configure( cfg ); // JGroups configuration for master node cfg.setProperty( "hibernate.search.default." + Environment.WORKER_BACKEND, "jgroupsMaster" ); cfg.setProperty( JGroupsChannelProvider.CLUSTER_NAME, CHANNEL_NAME ); cfg.setProperty( JGroupsChannelProvider.CONFIGURATION_FILE, "testing-flush-loopback.xml" ); } protected Class<?>[] getAnnotatedClasses() { return new Class[] { TShirt.class }; } }