View Javadoc
1   package org.apache.onami.persist.test.transaction.testframework;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import com.google.common.annotations.VisibleForTesting;
23  import com.google.inject.Injector;
24  import org.apache.onami.persist.EntityManagerProvider;
25  import org.apache.onami.persist.Transactional;
26  import org.apache.onami.persist.UnitOfWork;
27  import org.apache.onami.persist.test.TestEntity;
28  import org.apache.onami.persist.test.transaction.testframework.exceptions.RuntimeTestException;
29  import org.apache.onami.persist.test.transaction.testframework.exceptions.TestException;
30  
31  import javax.inject.Inject;
32  import java.lang.reflect.Method;
33  import java.util.ArrayList;
34  import java.util.List;
35  
36  import static com.google.common.base.Preconditions.checkNotNull;
37  import static com.google.common.base.Preconditions.checkState;
38  import static org.junit.Assert.assertNotNull;
39  import static org.junit.Assert.assertNull;
40  
41  /**
42   * Worker for transactional tests. The worker instantiates the {@link TransactionalTask} and
43   * executes them.
44   */
45  public class TransactionalWorker
46  {
47  
48      private static final String DO_TRANSACTIONAL = "doTransactional";
49  
50      private final TransactionalTasks tasks = new TransactionalTasks();
51  
52      private final List<TestEntity> storedEntities = new ArrayList<TestEntity>();
53  
54      @Inject
55      private Injector injector;
56  
57      @Inject
58      private UnitOfWork unitOfWork;
59  
60      @Inject
61      private EntityManagerProvider emProvider;
62  
63      /**
64       * Schedules a task for execution by this worker.
65       * If more than one task are scheduled they will be called in the order they have been
66       * scheduled.
67       *
68       * @param taskType the task to schedule for execution.
69       */
70      public void scheduleTask( Class<? extends TransactionalTask> taskType )
71      {
72          checkTransactionalAnnotation( taskType );
73          final TransactionalTask task = injector.getInstance( taskType );
74          task.setWorker( this );
75          tasks.add( task );
76      }
77  
78      private void checkTransactionalAnnotation( Class<? extends TransactionalTask> taskType )
79      {
80          try
81          {
82              final Method method = taskType.getMethod( DO_TRANSACTIONAL );
83              final Transactional annotation = method.getAnnotation( Transactional.class );
84              checkNotNull( annotation, "@Transactional annotation missing on %s.%s", taskType.getSimpleName(),
85                            method.getName() );
86          }
87          catch ( NoSuchMethodException e )
88          {
89              // should never occure.
90              throw new RuntimeException( e );
91          }
92      }
93  
94      /**
95       * Executes the previously specified tasks. All entities which were stored using
96       * {@link TransactionalTask#storeEntity(org.apache.onami.persist.test.TestEntity)} are collected by the worker.<p/>
97       */
98      public void doTasks()
99      {
100         checkState( tasks.hasTasks(), "no tasks have been added to the worker." );
101         checkState( tasks.hasNext(), "doTasks() has already been executed." );
102         checkState( !unitOfWork.isActive(), "Active UnitOfWork found." );
103 
104         try
105         {
106             doNextTask();
107         }
108         catch ( TestException e )
109         {
110             // do nothing
111         }
112         catch ( RuntimeTestException e )
113         {
114             // do nothing
115         }
116 
117         checkState( !tasks.hasNext(), "One of the tasks forgot to call doOtherTasks()." );
118         checkState( !unitOfWork.isActive(), "Active UnitOfWork after tasks found." );
119     }
120 
121     /**
122      * Check all stored entities if they actually have been persisted in the DB.
123      */
124     @Transactional
125     public void assertAllEntitiesHaveBeenPersisted()
126     {
127         checkState( !storedEntities.isEmpty(), "no entities to check" );
128         for ( TestEntity storedEntity : storedEntities )
129         {
130             assertNotNull( "At least one entity which should have been persisted was NOT found in the DB. " + tasks,
131                            emProvider.get().find( TestEntity.class, storedEntity.getId() ) );
132         }
133     }
134 
135     /**
136      * Check all stored entities if they actually have NOT been persisted in the DB.
137      */
138     @Transactional
139     public void assertNoEntityHasBeenPersisted()
140     {
141         checkState( !storedEntities.isEmpty(), "no entities to check" );
142         for ( TestEntity storedEntity : storedEntities )
143         {
144             assertNull( "At least one entity which should NOT have been persisted was found in the DB. " + tasks,
145                         emProvider.get().find( TestEntity.class, storedEntity.getId() ) );
146         }
147     }
148 
149     @VisibleForTesting
150     void doNextTask()
151         throws TestException
152     {
153         if ( tasks.hasNext() )
154         {
155             final TransactionalTask task = tasks.next();
156             try
157             {
158                 task.doTransactional();
159             }
160             finally
161             {
162                 storedEntities.addAll( task.getPersistedEntities() );
163             }
164         }
165     }
166 
167 
168     /**
169      * Class holding the tasks of a worker.
170      */
171     private static class TransactionalTasks
172     {
173 
174         private final List<TransactionalTask> tasks = new ArrayList<TransactionalTask>();
175 
176         private int pos = 0;
177 
178         /**
179          * @return {@code true} if there have already been tasks added.
180          */
181         public boolean hasTasks()
182         {
183             return !tasks.isEmpty();
184         }
185 
186         /**
187          * Add a task.
188          *
189          * @param task the task to add.
190          * @throws IllegalStateException if {@link #next()} has already been called on this instance.
191          */
192         public void add( TransactionalTask task )
193         {
194             checkState( pos == 0 );
195             tasks.add( task );
196         }
197 
198         /**
199          * @return {@code true} if there are more tasks.
200          */
201         public boolean hasNext()
202         {
203             return pos < tasks.size();
204         }
205 
206         /**
207          * @return the next task.
208          * @throws IndexOutOfBoundsException if there are no more tasks.
209          */
210         public TransactionalTask next()
211         {
212             final TransactionalTask result = tasks.get( pos );
213             pos++;
214             return result;
215         }
216 
217         @Override
218         public String toString()
219         {
220             final StringBuilder sb = new StringBuilder( "Tasks[" );
221             String separator = "";
222             for ( TransactionalTask t : tasks )
223             {
224                 sb.append( separator );
225                 final String taskType = t.getClass().getSimpleName();
226                 sb.append( taskType.replaceAll( "\\$\\$EnhancerByGuice\\$\\$.*", "" ) );
227                 separator = ", ";
228             }
229             sb.append( "]" );
230             return sb.toString();
231         }
232     }
233 
234 }